首页 > 解决方案 > NetLogo 5 到 6 过渡:for 循环

问题描述

我对 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 期望此输入是匿名报告者,但得到了一个匿名命令'。

经过大量阅读过渡指南编程指南字典和大量谷歌搜索后,我仍然无法弄清楚如何做到这一点。有什么建议么?

操作系统:MacOS High Sierra 版本 10.13.4

标签: netlogo

解决方案


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

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

(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 版本的 identity 函数:

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

更清楚,不是吗?


推荐阅读