繁体   English   中英

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

[英]Different outputs when using Seq.map and Array.map for the same code in F#

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

// 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|] |> Array.map fizzBuzzer.Check我得到了预期的输出:

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
val it : unit [] =
  [|(); (); (); (); (); (); (); (); (); (); (); (); (); (); ()|]

但是,当我使用[1..15] |> Seq.map fizzBuzzer.Check

1
2
Fizz
4
Buzz
val it : seq<unit> = seq [(); (); (); (); ...]

我不明白为什么两个输出应该不同。

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

如果您实际上要强制执行整个序列(因为要运行其副作用,例如打印到屏幕上),则应该使用Seq.iter而不是Seq.map 请注意, Seq.iter仅接受返回()函数(即unit类型),这清楚地表明,它正在寻找具有副作用的函数,而不是具有有意义的返回值的函数。

Seq.map创建一个惰性序列,直到使用时才对其元素进行求值。 如您所见, toString方法仅显示序列的前四个元素,后跟... 这样做是因为序列可以是无限的(并且无法分辨),因此,如果始终尝试显示所有元素,则可能导致无限循环。 由于它仅显示前四个元素,因此只需要评估这四个元素以及第五个元素(以便决定是否显示... ,我想)。 因此,只有在您遍历整个序列之前,这些函数才被执行。

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

一般来说,给map的函数通常没有副作用(或至少没有外部可见的副作用),因此,在准确评估函数的每次调用时,都没有关系。 当然,这里的情况并非如此。

此外,具有数组或单元序列几乎没有用处。 显然,您对调用函数的副作用比对函数的返回值更感兴趣。 因此,应该使用Seq.iter在原始数组的所有元素上调用函数,而无需创建任何新的数据结构,而不是使用map来创建您不感兴趣的数组或值序列。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM