Seq.filter和无限似乎违反直觉

时间:2015-11-10 06:20:12

标签: f#

我试图理解在无限列表上应用过滤器的结果。我需要一个解释来了解幕后发生的事情。

let nums = Seq.initInfinite id |> Seq.filter ((>) 0);;
nums |> Seq.take 0;; // of course, this works
nums |> Seq.take 1;; // this overflows

在我的阅读中,第一行意味着“在零处开始无限序列并过滤掉大于零的所有值(例如1,2,3等......)”。因此,获取零值将返回空集。好的,有道理。但是,如果我们取一个值,并且该值为零,显然不大于零,为什么溢出?

进一步尝试:

let nums = Seq.initInfinite (fun i -> i - 1) |> Seq.filter ((>) 0);;
nums |> Seq.take 1;; // this works and returns -1
nums |> Seq.take 2;; // this overflows

同样,当我读到这个时,零应该是序列可以迭代的有效值。

复杂化我的困惑:

let nums = Seq.initInfinite id |> Seq.filter ((>=) 0);;
nums |> Seq.take 1;; // works, [0]

也许我可以通过假设过滤器实际意味着调和行为,“值无效,除非它们是> = 0”。但这不值得:

let nums = Seq.initInfinite (fun i -> i - 1) |> Seq.filter ((>=) 0);;
nums |> Seq.take 1;; // works [-1]
nums |> Seq.take 2;; // works [-1, 0]

过滤器不能定义 有效 值...对于filter ((>=) 0),-1不是> = 0。

过滤器不能定义 无效 值...对于0不是>在filter ((>) 0)的情况下为0。

请为我解读这个。

2 个答案:

答案 0 :(得分:9)

由于你如何使用谓词,它实际上意味着什么令人困惑。让我们首先解决这个问题,方法是通过过滤器推送具有已知值集的有限序列。

> let input = [-5; 0; 5 ];;
val input : int list = [-5; 0; 5]

> let output = input |> Seq.filter ((>) 0) |> Seq.toList;;
val output : int list = [-5]

等等,为什么-5会被退回?这是因为(>) 0实际上不是x > 0,而是(>) 0 x,相当于0 > x。因此,Seq.filter ((>) 0)Seq.filter (fun i -> 0 > i)相同:

> let output = input |> Seq.filter (fun i -> 0 > i) |> Seq.toList;;
val output : int list = [-5]

现在我们已经弄明白了,让我们试着查看溢出。

let nums = Seq.initInfinite id |> Seq.filter ((>) 0);;

Seq.initInfinite id返回以0开头的无限集合,其下一个元素等于prev + 1

> Seq.initInfinite id |> Seq.take 5 |> Seq.toList;;
val it : int list = [0; 1; 2; 3; 4]  // this will keep going if you remove Seq.take

现在您正在添加过滤器,它只返回小于0的元素。这有多少元素?的无!即可。因此,您的过滤器将继续询问Seq.initInfinite中的下一个元素,直到您溢出Integer范围并抛出异常。

答案 1 :(得分:0)

序列被懒惰地评估,所以乍一看这种行为似乎有点奇怪,因为你没有调用toList,toArray等。

实际上它只是溢出,因为你正在使用F#interactive,当你按下返回时会尝试打印序列的前几个元素。因此,它会尝试遍历您的无限序列,直到找到一些与您的谓词匹配的项目,并且永远不会找到任何项目,因此溢出。