NetLogo с 5 по 6 переход: для цикла - PullRequest
0 голосов
/ 14 мая 2018

Я новичок в NetLogo и пытаюсь преобразовать модель, построенную в NetLogo 5.3.1, в NetLogo 6.0.3. Автоконвертер не работает, поэтому я пытаюсь вручную преобразовать задачи в анонимные. Однако я застрял при преобразовании следующего раздела кода NL-5.3.1:

let tempNewList [ ]                                           ; to store the new list
  (foreach n-values (highest-family-id + 1) [?] [             ; search through all family-ids ever created in model
      if count turtles with [family-id = ?] >= 2 [            ; if family-id belongs to 2 or more turtles... 
        set tempNewList lput ? tempNewList                    ; ...  add to list
      ]
  ])
  set families tempNewList

Здесь цель состоит в том, чтобы обновить глобальную переменную 'family', которая представляет собой список идентификаторов семейства, которые имеют по меньшей мере 2 человека. При открытии в NL-6.3.0 этот код выдает ошибку: «? Не определено», как и ожидалось.

Я переименовал в "?" to eachFamilyID и использовал новый синтаксис "->" для анонимных процедур. Например, я попытался изменить код выше:

let tempNewList [ ]                                                       
  (foreach n-values (highest-family-id + 1) [ eachFamilyID ->  
    if count turtles with [family-id = eachFamilyID] >= 2 
    [set tempNewList lput eachFamilyID tempNewList ]  ]                
    ])
set families tempNewList

Это дает ошибку: «N-VALUES ожидал, что этот ввод будет анонимным репортером, но вместо этого получил анонимную команду».

После большого прочтения руководства по переходу , руководства по программированию , словарь и большого количества поиска в Google, я все еще не могу понять, как это сделать этот. Есть предложения?

Операционная система: MacOS High Sierra Версия 10.13.4

1 Ответ

0 голосов
/ 14 мая 2018

Вы столкнулись с простой проблемой синтаксиса, но я также думаю, что, возможно, стоит сделать шаг назад и переосмыслить свой подход к проблеме. Давайте начнем с синтаксиса.

Если вы посмотрите на свой необращенный код, вы заметите, что структура верхнего уровня выглядит примерно так:

(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 ]

Намного понятнее, нет?

...