在单行中生成F#中的一系列点

时间:2014-03-05 08:43:31

标签: .net f# functional-programming

我发现自己经常实施相同的(x, y)模式:

let rectangleSizes = seq {
    for w = 1 to width do
        for h = 1 to height do
            yield (w, h)
}

这不是偶然的吗?当然我只能在一行中编写这个相同的函数,但我觉得它的可读性会受到很大的影响:

let rectangleSizes = seq { for w = 1 to width do for h = 1 to height do yield (w, h) }

5 个答案:

答案 0 :(得分:9)

如果我一直有初始化,我会定义自己的运算符:

let (..) (x0,y0) (xn,yn) =  
    seq {
        for x = x0 to xn do
            for y = y0 to yn do
                yield (x, y)}

let rectangleSizes = {(1,1) .. (5,7)}

但这会影响原始(..)运算符,但您可以使用其他运算符名称或函数。还有一个trick可以避免隐藏原始运算符定义。

或者,如果您使用实现Applicative Functors的库,例如F#+,您可以在一行中将其定义为:

let rectangleSizes = (fun x y -> (x, y)) <!> {1..width} <*> {1..height}

注意:函数fun x y -> (x, y)通常称为tuple2

#r @"FsControl.Core.dll"
#r @"FSharpPlus.dll"

open FSharpPlus
let tuple2 a b = (a,b)
let width, height = 5,7

let rectangleSizes = tuple2 <!> {1..width} <*> {1..height}

答案 1 :(得分:2)

您可以使用2D数组实现IEnumerable的事实,可以使用IEnumerable<'T>转换为seq<'T>(又名Seq.cast):

let rectangleSizes = Array2D.initBased 1 1 width height (fun w h -> (w, h)) |> Seq.cast<int * int>

编辑:这将创建一个存储所有元素的数组,而您的初始实现会根据需要生成它们。如果您的宽度和高度很大,这可能会消耗太多内存。

答案 2 :(得分:2)

你可以通过以下方式节省一点空间:

let rectangleSizes = seq {
    for w = 1 to width do 
       for h = 1 to height -> (w, h)
}

答案 3 :(得分:2)

另一种可能性是写

Seq.init width (fun w -> Seq.init height (fun h -> (w+1,h+1))) |> Seq.concat

seq [1 .. width] |> Seq.collect (fun w -> Seq.init height (fun h -> (w,h+1)))

答案 4 :(得分:1)

您可以使用List.collectList.map

来实现这一目标
let rectangleSizes = [1..width] |> List.collect (fun x -> [1..height] |> List.map (fun y -> (x,y)))

但是在我看来,这不像使用seq构造函数的原始解决方案那样可读(并且评估非常渴望)。我也更喜欢@ Gustavo的solution使用重载运算符。

<强>更新

使用序列进行延迟评估:

let rectangleSizes = {1..width} |> Seq.collect (fun x -> {1..height} |> Seq.map (fun y -> (x,y)))