F# 对F中的同一代码使用Seq.map和Array.map时的不同输出#

F# 对F中的同一代码使用Seq.map和Array.map时的不同输出#,f#,F#,我有一个简单的基于事件的FizzBuzz实现,如下所示 // Events in F# type FizzBuzz() = let _event = Event<int>() member this.Event = _event.Publish member this.Check n = _event.Trigger n // Instantiate let fizzBuzzer = FizzBuzz() // Add an event handle

我有一个简单的基于事件的FizzBuzz实现,如下所示

// Events in F#

type FizzBuzz() =
    let _event = Event<int>()

    member this.Event = _event.Publish
    member this.Check n = _event.Trigger n 

// Instantiate
let fizzBuzzer = FizzBuzz()

// Add an event handler
fizzBuzzer.Event.Add (function
    | x when x%5=0 && x%3=0 -> printfn "FizzBuzz"
    | x when x%3=0 -> printfn "Fizz"
    | x when x%5=0 -> printfn "Buzz"
    | x -> printfn "%d" x) 
但是,当我用
[1..15]|>Seq.map fizzBuzzer.Check测试它时,我有

1
2
Fizz
4
Buzz
val it : seq<unit> = seq [(); (); (); (); ...]
1
2.
起泡
4.
嗡嗡声
val it:seq=seq[();();();();();…]
我不明白为什么这两个输出应该是不同的

F#序列是惰性的,因此它们只能根据消费代码的需要进行迭代。当您使用
Seq.map
函数时,它会生成一个尚未迭代的惰性序列。当F#Interactive得到一个序列作为表达式的结果时,它故意不打印整个序列,因为该序列可能是无限的,或者可能需要很长时间来计算。相反,它获取序列的第一个四个元素并打印它们。然后检查序列是否有更多的值(即序列枚举器上的
.MoveNext()
函数返回
true
),如果有,则在末尾打印
..
。因此,序列实际上会计算出五个值,尽管您只会看到其中的四个

如果你真的想强制执行整个序列(因为你想让它的副作用,比如打印到屏幕上),那么你应该使用
Seq.iter
而不是
Seq.map
。请注意,
Seq.iter
只接受返回
()
(即
单元
类型)的函数,这表明它寻找的是具有副作用的函数,而不是具有有意义返回值的函数。

F#序列是惰性的,因此它们只在消费代码需要时进行迭代。当您使用
Seq.map
函数时,它会生成一个尚未迭代的惰性序列。当F#Interactive得到一个序列作为表达式的结果时,它故意不打印整个序列,因为该序列可能是无限的,或者可能需要很长时间来计算。相反,它获取序列的第一个四个元素并打印它们。然后检查序列是否有更多的值(即序列枚举器上的
.MoveNext()
函数返回
true
),如果有,则在末尾打印
..
。因此,序列实际上会计算出五个值,尽管您只会看到其中的四个


如果你真的想强制执行整个序列(因为你想让它的副作用,比如打印到屏幕上),那么你应该使用
Seq.iter
而不是
Seq.map
。请注意,
Seq.iter
只接受返回
()
(即
单元
类型)的函数,这表明它寻找的是具有副作用的函数,而不是具有有意义返回值的函数。

Seq.map
创建了一个惰性序列,其元素在使用之前不会被评估。如您所见,
toString
方法仅显示序列的前四个元素,后跟
。它这样做是因为序列可以是无限的(而且没有办法判断),所以如果它总是试图显示所有元素,那么可能会导致无限循环。因为它只显示前四个元素,所以只需要计算这四个元素加上第五个元素(我想,为了决定是否显示
)。因此,在遍历整个序列之前,这些是函数执行的唯一时间

Array.map
另一方面创建一个数组,它是一个严格的数据结构。因此,在创建数组时,数组的所有元素都存在

一般来说,赋予
map
的函数通常不会有副作用(或者至少不会有外部可见的副作用),因此,准确地评估函数的每次调用时,应该无关紧要。当然,这里的情况并非如此

此外,拥有一个数组或一系列单元几乎没有什么用处。显然,您对调用函数的副作用比对其返回值更感兴趣。因此,与其使用
map
创建您不感兴趣的数组或值序列,不如使用
Seq.iter
对原始数组的所有元素调用函数,而不创建任何新的数据结构


或者,您也可以使用
map
来创建字符串数组或序列,而不是单元。

Seq.map
创建一个惰性序列,其元素在使用之前不会被计算。如您所见,
toString
方法仅显示序列的前四个元素,后跟
。它这样做是因为序列可以是无限的(而且没有办法判断),所以如果它总是试图显示所有元素,那么可能会导致无限循环。因为它只显示前四个元素,所以只需要计算这四个元素加上第五个元素(我想,为了决定是否显示
)。因此,在遍历整个序列之前,这些是函数执行的唯一时间

Array.map
另一方面创建一个数组,它是一个严格的数据结构。因此,在创建数组时,数组的所有元素都存在

一般来说,赋予
map
的函数通常不会有副作用(或者至少不会有外部可见的副作用),因此,准确地评估函数的每次调用时,应该无关紧要。当然,这里的情况并非如此

此外,拥有一个数组或一系列单元几乎没有什么用处。显然,您对调用函数的副作用比对其返回值更感兴趣。因此,与其使用
map
创建您不感兴趣的数组或值序列,不如使用
Se
1
2
Fizz
4
Buzz
val it : seq<unit> = seq [(); (); (); (); ...]