2014-03-05 63 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