NetLogo 5到6转换:for循环

时间:2018-05-14 14:47:30

标签: netlogo

我是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

这里,目标是更新'家族'全局变量,该变量是至少2个人拥有的家庭ID列表。在NL-6.3.0中打开时,此代码会出现错误:“?未定义”,如预期的那样。

我改名为“?”到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期望这个输入是一个匿名的记者,但是得到了一个匿名命令'。

经过大量阅读transition guideprogramming guidedictionary和大量谷歌搜索,我仍然无法弄清楚如何做到这一点。有什么建议吗?

操作系统:MacOS High Sierra版本10.13.4

1 个答案:

答案 0 :(得分:4)

您面临着一个简单的语法问题,但我也认为值得退一步并重新思考您解决问题的方法。让我们从语法开始。

如果您查看未转换的代码,您会注意到顶级结构是这样的:

(foreach n-values (highest-family-id + 1) [?] [ ... ])

此处使用了两个主要基元,foreachn-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 -> ... ])

这应该可以解决您当前的问题。

现在让我们退一步。您要做的是构建一个仅包含与特定条件相对应的值的列表。在您的情况下,您只想保留至少有两个成员的家庭的家庭ID。 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

这有点慢,但仍然可以保证为您提供所有正在使用的家庭ID,并且您可以避免一个人退出一个"错误。

但如果您愿意使用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的代表次数,这正是我们需要的信息!该信息存储在"表格中。关联"键"有价值的。在这种情况下,每个家庭ID都是一个键,值是它出现的次数。

有了这个,我们需要做的就是进行一些过滤,只保留值至少为2的键。表扩展没有用于过滤表的原语,但我们可以轻松地将表转换为列表,使用table:to-list,然后过滤该列表。

table:to-list的结果是列表列表,其中每个子列表都有两个元素,对应于原始表中的键值对。假设我们只有两个家庭,家庭1,有5个成员,家庭2,只有1个成员。我们将得到以下列表:[[1 5] [2 1]]。够容易!现在,如果我们对此使用filter,我们只需要保留该对中第二个成员(即last一个)为>= 2的子列表。这是上面代码的filter [ p -> last p >= 2 ] ...部分。

一旦过滤了列表,最后一步就是:我们只想要保留每个子列表的第一个元素。将列表转换为其他内容(这是我们想要在此处执行的操作)通常使用map原语完成,该原语将记者应用于列表的每个元素并返回结果列表。在这种情况下,我们直接将first报告者传递给它,但我们也可以使用像[ p -> first p ]这样的匿名记者。

如果这是我的模特,我会采取完全不同的方法。数字ID是一种难以处理的问题,也是代码中的一个重要错误来源。 NetLogo有更好的方式来表示事物之间的关系。我会创建两个不同的海龟品种:personsfamilies,并在人与他们所属的家庭之间建立联系。获得至少有两名成员的家庭就是:

families with [ count my-links >= 2 ]

更清楚,不是吗?