【问题标题】:modifying an element of a list in-place in J, can it be done?在J中就地修改列表的元素,可以吗?
【发布时间】:2011-09-16 23:24:13
【问题描述】:

我一直在使用 J 中的lookandsay (OEIS A005150) 实现。我制作了两个版本,都非常简单,使用while. 类型控制结构。一个重复,另一个循环。因为我有强迫症,所以我开始对版本进行比较计时。

看一看就是序列 1 11 21 1211 111221 s,一一,二一等。

对于列表的早期元素(最多 20 个左右),循环版本获胜,但数量很少。大约 30 的时间会导致递归版本获胜,如果堆栈空间足够支持递归版本,则可能会优先使用递归版本。我研究了原因,我相信这与处理中间结果有关。序列中的第 30 个数字有 5808 位。 (第 32 个数字,9898 位,第 34 个,16774。)

当你用递归解决问题时,你可以在递归调用中保存中间结果,最后的unstacking构建结果,以便对结果进行最少的处理。

在列表版本中,您需要一个变量来保存结果。每次循环迭代都会导致您需要在结果中添加两个元素。

在我看来,问题是我无法在 J 中找到任何方法来修改现有数组而不完全重新分配它。所以我说

try. o =. o,e,(0&{y) catch. o =. e,(0&{y) end.

将一个元素放入 o 中,当我们开始时 o 可能没有值。这可能比

o =. i.0
.
.
.
o =. (,o),e,(0&{y)

关键在于,如果没有散布,结果就会出现错误的形状,或者看起来是这样。它以某种方式从 i.0 继承了一个形状。

但即使像 } amend 这样的函数也不会修改列表,它们会返回一个对其进行了修改的列表,如果要保存该列表,则需要对其进行分配。随着分配列表的大小增加(当您将数字从头到尾移动到下一个数字时),分配似乎需要更多时间和更多时间。这个赋值确实是我唯一能看到的,它会使元素 32、9898 位在递归版本中花费更少的时间,而元素 20(408 位)在循环版本中花费更少的时间。

递归版本通过以下方式构建返回:

e,(0&{y),(,lookandsay e }. y)

上面的行既是函数的返回行,也是递归的,所以整个返回向量在调用到达字符串末尾并且所有内容都取消堆栈时立即构建。

在 APL 中,我认为可以按以下顺序说:

 a[1+rho a] <- new element

但是当我在 NARS2000 中尝试这个时,我发现它会导致索引错误。我无法访问任何其他 APL,我可能记得 APL Plus 中的这个成语,我怀疑它在 APL\360 或 APL\1130 中是否以这种方式工作。我可能完全记错了。

我在 J 中找不到这样做的方法。可能没有办法这样做,但下一个想法是预先分配一个可以保存结果的数组,并更改单个条目。我也看不出有什么办法——也就是说,J 似乎不支持 APL 成语:

a<- iota 5
a[3] <- -1

这是因为语言纯度而被禁止的副作用之一吗?

解释器是否将 a=. a,foo 或其某些变体识别为它应该在内部快速路径到 a[&gt;:#a]=.foo 的东西?

这是递归版本,只是为了它。我尝试了很多不同的版本,我相信程序越长越慢,通常越复杂越慢。一般来说,程序可以被链接起来,这样如果你想要第 n 个数字,你可以做lookandsay^: n ] y。我已经尝试了许多优化,但我遇到的问题是我无法判断我将输出发送到什么环境。如果我知道我将它发送到程序的下一次迭代,我会将它作为一个数字数组而不是一个大数字发送。

我还怀疑,如果我能弄清楚如何制作一个默认版本的代码,它会运行得更快,因为我发现当我在代码中添加一些应该让它更短的东西时,它会运行得更长。

lookandsay=: 3 : 0
if. 0 = # ,y do.  return. end. NB. return on empty argument
if. 1 ~: #@$ y do.  NB. convert rank 0 argument to list of digits
y =. (10&#.^:_1) x: y
f =. 1
assert. 1 = #@$ y NB. the converted argument must be rank 1
else.
NB. yw =. y
f =. 0
end.
NB. e should be a count of the digits that match the leading digit.
e=.+/*./\y=0&{y

if. f do.

o=. e,(0&{y),(,lookandsay e }. y)
assert. e = 0&{ o
10&#. x: o
return.
else. 
e,(0&{y),(,lookandsay e }. y)
return.
end.
)

我对产生的数字的特征很感兴趣。我发现如果你从 1 开始,数字永远不会高于 3。如果你从高于 3 的数字开始,它将作为单例存在,你也可以通过从某事开始将一个数字放入生成的数字中就像 888888888 一样,它将生成一个数字,其中包含一个 9,数字末尾有一个 8。但除了单例之外,没有一个数字高于 3。

编辑: 我又做了一些测量。我最初编写程序以接受向量或标量,其想法是在内部我将使用向量。我曾考虑将向量从一层代码传递到另一层,但我仍然可能使用左参数来控制代码。当我通过顶层向量时,代码运行得非常快,所以我的猜测是大部分 CPU 都被从向量转换为数字的长数字所消耗。递归例程在递归时总是向下传递一个向量,这可能就是它几乎与循环一样快的原因。

这并没有改变我的问题。

我有一个答案,但我不能在三个小时内发布。那我就贴出来,请不要做大量的研究来回答它。

【问题讨论】:

  • 我最近注意到的一件事是在两个布尔矩阵之间使用 And (*.) 的简单尝试导致内存崩溃,因为这两个矩阵中的一个恰好存储为整数(尽管它仍然只是0 和 1)。强制提前转换为布尔值(只需 1=)解决了这个问题。我检查了,i.0 解析为布尔值。我想知道你是否会通过欺骗 J 使其成为布尔值来获得更好的结果。使用0$2i.2-2
  • 这不是jsoftware.com/jwiki/Essays/In-Place%20Operations主要覆盖的吗?

标签: functional-programming j tacit-programming


【解决方案1】:

类似的作业

arr=. 'z' 15} arr

在原地执行。 (有关其他支持的就地操作,请参阅JWiki article) 解释器确定只有一小部分 arr 被更新,并且不会创建要重新分配的整个新列表。

在您的情况下发生的不是数组被重新分配,而是它以小增量多次增长,导致内存分配和重新分配。

如果您预先分配(通过为其分配一些大数据块),那么您可以使用} 对其进行修改而不会造成太多损失。

【讨论】:

    【解决方案2】:

    在我问了这个问题之后,老实说,我忘记了这个网站。

    是的,答案是该语言没有表示“就地更新,但如果您使用两种形式”的形式

    x =: x ,几乎任何东西

    x =: 几乎任何东西 } x

    然后解释器将它们识别为特殊的并在原地更新,除非它不能。口译员还可以识别许多其他特殊功能,例如:

    199(1000&|@^)199

    这种组合运算是模幂运算。它从不计算整个幂,因为

    199(1000&|^)199

    would - 以 _ 结尾,没有 @。

    因此,关于特价商品的文章值得一读。我会标记别人的答案。

    【讨论】:

      【解决方案3】:

      上面提供的链接 (http://www.jsoftware.com/jwiki/Essays/In-Place%20Operations) 显示了支持修改现有数组而不是创建新数组的各种操作。它们包括:

          myarray=: myarray,'blah'
      

      如果您对 lookandsay 序列的默认版本感兴趣,请参阅 this submission to RosettaCode:

         las=: ,@((# , {.);.1~ 1 , 2 ~:/\ ])&.(10x&#.inv)@]^:(1+i.@[)
         5 las 1
      11 21 1211 111221 312211
      

      【讨论】:

      • las=: ,@((# , {.);.1~ 1 , 2 ~:/\ ])&amp;.(10x&amp;#.inv)@]^:(1+i.@[) las 12334 |domain error: las | las 12334 las 1 |domain error: las | las 1 las=: 3 : ,@((# , {.);.1~ 1 , 2 ~:/\ ])&amp;.(10x&amp;#.inv)@]^:(1+i.@[) |domain error | las=: 3 :,@((#,{.);.1~1,2~:/\])&amp;.(10&amp;#.inv)@]^:(1+i.@[) las=: 3 : ',@((# , {.);.1~ 1 , 2 ~:/\ ])&amp;.(10x&amp;#.inv)@]^:(1+i.@[)' las 1 |syntax error: las | las 1 las '11' |syntax error: las | las'11' las 1 1 |syntax error: las | las 1 1
      • 我已经编辑了我的答案以包含一个使用 las 的示例。或者,您可以查看同样记录动词使用的 RosettaCode 条目。
      猜你喜欢
      • 2015-11-07
      • 2011-03-06
      • 2020-02-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-16
      • 2011-09-14
      • 2012-11-11
      • 2019-11-10
      相关资源
      最近更新 更多