2014-03-05 129 views
5

我发现自己经常执行相同(x, y)图形生成F#中的点的序列:在一个班轮

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) } 
+0

我有第二代码片段的所有编辑,因为它使我的眼睛流血,然后我意识到这是你的问题的关键:d –

回答

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以避免阴影原始的操作符定义。

另外,如果你使用它实现了应用型函子像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

我觉得没有可用来避免阴影库定义的范围内操作技巧,因此这根本不是一个好主意。理论上你可以在一个类型上提供一个用户定义的'op_Range',但是如果你尝试使用它,你会在binding._错误中得到一个_Unexpected符号'..'。可能由于规范的§3.6,指定它被视为符号关键字。 – kaefer

+1

@kaefer无论如何,元组是一种已经定义的类型,仍然有一个技巧可以使它工作,我只是上传了一个示例https://gist.github.com/gmpl/d66d1c2a3ac7972f3305也会将它链接到答案。 – Gustavo

2

你可以使用二维数组实现IEnumerable,事实上它可以转化为IEnumerable<'T> (又名seq<'T>)使用Seq.cast

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

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

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))) 
+0

因为您使用List.map和List.collect函数,所以评估非常重要,请改用Seq.map和Seq.collect函数。您还需要使用大括号而不是方形。 – Gustavo

+0

你是对的,我不知道构造函数'n..m'也存在于序列中。 – polkduran

2

您可以通过节省的空间却有点:

let rectangleSizes = seq { 
    for w = 1 to width do 
     for h = 1 to height -> (w, h) 
} 
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))) 
+1

小简化:'Seq.collect id'是'Seq.concat'。 – Tarmil

+0

我怎么没有意识到这一点?谢谢!:) – torbonde