Вы столкнулись с простой проблемой синтаксиса, но я также думаю, что, возможно, стоит сделать шаг назад и переосмыслить свой подход к проблеме. Давайте начнем с синтаксиса.
Если вы посмотрите на свой необращенный код, вы заметите, что структура верхнего уровня выглядит примерно так:
(foreach n-values (highest-family-id + 1) [?] [ ... ])
Здесь используются два основных примитива, foreach
и n-values
, и оба они принимают параметр задачи.
В случае foreach
эта задача представляет собой весь блок [ if count turtles ... ]
, который я здесь выбрал [ ... ]
.
В случае n-values
эта задача является просто функцией идентификации [?]
, которая просто возвращает вам все, что ей передано. Например, n-values 5 [?]
даст вам список [0 1 2 3 4]
, потому что это последовательные значения, переданные как ?
на n-values
его аргументу задачи.
Синтаксис изменился в NetLogo 6, но n-values
все еще нужен какой-то способ генерирования последовательных значений. Теперь посмотрите на вашу конвертированную версию:
(foreach n-values (highest-family-id + 1) [ eachFamilyID -> ... ])
Вы видите, чего не хватает? Есть только одна анонимная процедура! Ваш вызов n-values
пытается использовать анонимную команду, предназначенную для foreach
, которая объясняет сообщение об ошибке, которое вы получаете. Чтобы это исправить, вы можете просто добавить версию NetLogo 6 функции идентификации в ваш код:
(foreach n-values (highest-family-id + 1) [ n -> n ] [ eachFamilyID -> ... ])
Это должно исправить вашу непосредственную проблему.
Теперь давайте сделаем этот шаг назад. То, что вы пытаетесь сделать, - это создать список только с теми значениями, которые соответствуют определенным критериям. В вашем случае вы хотите сохранить только семейные идентификаторы для семей, в которых есть как минимум два члена. NetLogo имеет встроенный примитив, который делает именно такие вещи: filter
. Вот один из способов его использования, который относительно близок к тому, что вы уже делаете:
let all-ids n-values (highest-family-id + 1) [ n -> n ]
set families filter [ id ->
count turtles with [ family-id = eachFamilyID ] >= 2
] all-ids
Разве это уже не лучше? Это все еще может быть улучшено, однако. Во-первых, NetLogo 6 имеет примитив range
, который вы часто можете использовать вместо n-values
:
let all-ids range (highest-family-id + 1)
Это аккуратно. Но вы также можете сделать что-то вроде этого:
let all-ids remove-duplicates [ family-id ] of turtles
Это немного медленнее, но все равно гарантированно предоставит вам все используемые семейные идентификаторы, и вы избежите возможности ошибки "отключено одним".
Но есть более аккуратный подход, который вы можете использовать, если хотите использовать расширение table
. Он включает в себя table:counts
примитив. Вот как вы могли бы использовать его, предполагая, что у вас есть extensions [ table ]
в верхней части вашего кода:
let counts table:counts [ family-id ] of turtles
set families map first filter [ p -> last p >= 2 ] table:to-list counts
Это выглядит немного загадочно, но преимущество в том, что он намного быстрее, чем другие подходы (и несколько элегантен, когда вы его понимаете). Позвольте мне немного его распаковать.
Первая строка достаточно проста: она использует table:counts
, чтобы подсчитать, сколько раз каждый family-id
представлен во всех наших черепахах, и это именно та информация, которая нам нужна! Эта информация хранится в «таблице», которая связывает «ключи» со значениями. В этом случае каждый идентификатор семьи является ключом, а значение - сколько раз оно появляется.
Как только мы это получим, все, что нам нужно сделать, - это некоторая фильтрация, чтобы сохранить только ключи, для которых значение равно как минимум 2. Расширение таблицы не имеет примитива для фильтрации таблиц, но мы можем легко превратить таблицу в список. , используя table:to-list
, а затем отфильтруйте этот список.
Результатом table:to-list
является список списков, где каждый подсписок имеет два элемента, соответствующих паре ключ-значение из исходной таблицы. Предположим, у нас есть только две семьи, семья 1, которая имеет 5 членов, и семья 2, которая имеет только 1 члена. Мы получили бы следующий список: [[1 5] [2 1]]
. Достаточно просто! Теперь, если мы используем filter
для этого, нам нужно сохранить только те списки, в которых второй член пары (то есть last
one) равен >= 2
. Это filter [ p -> last p >= 2 ] ...
часть кода выше.
После того, как список отфильтрован, есть один последний шаг: нам нужен только первый элемент каждого из сохраненных нами подсписков.Преобразование списка во что-то другое (что мы и хотим сделать здесь) обычно выполняется с помощью примитива map
, который берет репортера, применяет его к каждому элементу списка и возвращает полученный список,В этом случае мы напрямую передаем его репортеру first
, но мы могли бы также использовать анонимного репортера, например [ p -> first p ]
.
Если бы это была моя модель, я бы взялсовершенно другой подход.Числовые идентификаторы - боль в работе и большой источник ошибок в коде.NetLogo имеет лучшие способы представить отношения между вещами.Я хотел бы создать две разные породы черепах: persons
и families
и создать связи между людьми и семьей, к которой они принадлежат.В таком случае получить семьи, состоящие как минимум из двух человек, будет:
families with [ count my-links >= 2 ]
Намного понятнее, нет?