使用函数值生成新序列

Generate a new sequence with values from a function

根据标题,我有以下代码:

proc squared(n: int64): int64 = n * n

echo squared(5)

生成以下输出:

25

但是,如果我知道想要使用 squared 填充序列,我会这样写:

import sequtils

proc squared_seq(n: int64): seq[int64] =
    result = newSeq[int64](n)
    for i in 0 ..< n:
        result[i] = squared(i)

echo squared_seq(5)

我希望这会产生以下输出:

@[0, 1, 4, 9, 16]

但我得到的只是以下错误(在 result[i] = ... 行):

Error: type mismatch: got <seq[int64], int64, int64>
but expected one of: 
proc `[]=`(s: var string; i: BackwardsIndex; x: char)
proc `[]=`[T, U](s: var string; x: HSlice[T, U]; b: string)
proc `[]=`[T](s: var openArray[T]; i: BackwardsIndex; x: T)
proc `[]=`[Idx, T, U, V](a: var array[Idx, T]; x: HSlice[U, V]; b: openArray[T])
template `[]=`(s: string; i: int; val: char)
proc `[]=`[I: Ordinal; T, S](a: T; i: I; x: S)
proc `[]=`[T, U, V](s: var seq[T]; x: HSlice[U, V]; b: openArray[T])
proc `[]=`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T)

最终,这是某种形式的映射,因此我认为这段代码可以工作:

var arr = toSeq(0 ..< 5)
var sq_arr = map(arr, squared)

echo sq_arr

与之前相同的预期输出:

@[0, 1, 4, 9, 16]

但我得到的是(在 map 行):

Error: type mismatch: got <seq[int], proc (n: int64): int64{.noSideEffect, gcsafe, locks: 0.}>
but expected one of: 
proc map[T](s: var openArray[T]; op: proc (x: var T) {.closure.})
first type mismatch at position: 2
required type: proc (x: var T){.closure.}
but expression 'squared' is of type: proc (n: int64): int64{.noSideEffect, gcsafe, locks: 0.}
proc map[T, S](s: openArray[T]; op: proc (x: T): S {.closure.}): seq[S]
first type mismatch at position: 2
required type: proc (x: T): S{.closure.}
but expression 'squared' is of type: proc (n: int64): int64{.noSideEffect, gcsafe, locks: 0.}

我做错了什么?

(我使用的是 Nim 0.19.0,但它也不适用于 Nim 0.18.0)。

错误发生是因为您试图使用 int64 来索引 result 序列。使用通用 int 平台类型访问序列,depending on your platform 可能是 32 位长或 64 位长。您可以将 squared_seq 参数更改为 int 并且它应该编译:

import sequtils

proc squared(n: int64): int64 = n * n

proc squared_seq(n: int): seq[int64] =
    result = newSeq[int64](n)
    for i in 0 ..< n:
        result[i] = squared(i)

echo squared_seq(5)

或者,您可以将 int64 转换为 for i in 0 ..< int(n),但这可能很危险,具体取决于您传递给 proc 的值。

(感谢来自 freenode 中#nim 的@leorize、@mratsim 和@alehander42)。

第一个解决方案的问题是 seq 的索引必须是 int 并且 int64int 的转换不是自动的。 像下面这样的东西会起作用,因为 squared 的签名所需的 intint64 是自动的。

import sequtils

proc squared_seq(n: int64): seq[int64] =
    result = newSeq[int64](n)
    for i in 0 ..< int(n):
        result[i] = squared(i)

或者,以下方法也有效:

proc squared_seq(n: int64): seq[int64] =
    result = newSeq[int64](n)
    for i in 0 ..< n:
        result[int(i)] = squared(i)

但是我不太确定,如果以后只支持 int 索引,为什么 result = newSeq[int64](n) 不需要 nint。 不过,这似乎是一个不需要的功能。

关于映射方法,这里的问题是输入类型是int,而squared()需要int64。在这种情况下,显然最好的方法是对 intint64 序列使用某种代理方式,例如:

var arr = toSeq(0 ..< 5)
var sq_arr = map(arr, proc(x: int): int64 = squared(x))

或者,更好的是,强制 toSeq 产生 int64,例如:

var arr = toSeq(0'i64 ..< 5'i64)
var sq_arr = map(arr, squared)

或者更好,使用 map 的类型放宽版本,即 mapIt:

var arr = toSeq(0 ..< 5)
var sq_arr = mapIt(arr, squared(it))

这也应该会产生更快的代码。