2014-03-30 19 views
5

在APL中,可以使用位向量來選擇另一個向量的元素;這被稱爲壓縮。例如1 0 1 5 5 7將產生3 7.如何在F#中做什麼在APL中稱爲壓縮?

在函數式編程中是否有一個公認的術語,特別是F#?

這裏是我的F#程序:

let list1 = [|"Bob"; "Mary"; "Sue"|] 
let list2 = [|1; 0; 1|] 

[<EntryPoint>] 
let main argv = 

    0 // return an integer exit code 

我想什麼做的是計算出一個新的String []這將是[| 「鮑勃」;蘇「|]

一個將如何在F#中做到這一點

回答

6
Array.zip list1 list2     // [|("Bob",1); ("Mary",0); ("Sue",1)|] 
|> Array.filter (fun (_,x) -> x = 1) // [|("Bob", 1); ("Sue", 1)|] 
|> Array.map fst      // [|"Bob"; "Sue"|] 

管道運營商|>做功能應用語法逆轉,x |> f相當於f x作爲另一個答覆中提到,以取代ArraySeq避免構造中間陣列

我希望你會發現很多F#中缺少的APL原語對於列表和序列,許多可以通過從SeqArrayList模塊,如上所述。作爲參考,Seq模塊的here is an overview

+4

如果你是一個無點風格的粉絲,可以使用snd >>(=)1'代替。 – ildjarn

1

我認爲最簡單的就是使用數組序列的表達,這樣的事情:

let compress bits values = 
    [| 
     for i = 0 to bits.Length - 1 do 
      if bits.[i] = 1 then 
       yield values.[i] 
    |] 

如果你只是想使用組合程序,這是我會做什麼:

Seq.zip bits values 
|> Seq.choose (fun (bit, value) -> 
    if bit = 1 then Some value else None) 
|> Array.ofSeq 

我使用Seq函數而不是Array以避免構建中間數組,但它也是正確的。

1

有人可能會說,這是更地道:

Seq.map2 (fun l1 l2 -> if l2 = 1 then Some(l1) else None) list1 list2 
|> Seq.choose id 
|> Seq.toArray 

EDIT(用於管道愛好者)

(list1, list2) 
||> Seq.map2 (fun l1 l2 -> if l2 = 1 then Some(l1) else None) 
|> Seq.choose id 
|> Seq.toArray 
0

瑟倫Debois的解決方案是好的,但正如他所指出的,但我們可以做更好。讓我們定義一個函數,基於索倫的代碼:

let compressArray vals idx = 
    Array.zip vals idx 
     |> Array.filter (fun (_, x) -> x = 1) 
     |> Array.map fst 

compressArray最終建立在每個3行中的一個新的數組。如果輸入數組很長(對於快速測試中的10M值,則爲1.4秒),這可能需要一些時間。

let compressSeq vals idx = 
    Seq.zip vals idx 
     |> Seq.filter (fun (_, x) -> x = 1) 
     |> Seq.map fst 

此函數是通用的,對陣列,列表等將工作要生成一個陣列作爲輸出:


我們可以通過工作序列和僅在端創建陣列節省一些時間
compressSeq sq idx |> Seq.toArray 

後者節省了大約40%的計算時間(在我的測試中0.8s)。

作爲ildjarn評論的,該函數的參數來filter可以重寫到snd >> (=) 1,雖然導致可能是因爲所產生的額外的函數調用的一個輕微的性能下降(< 10%)。

+0

我知道,我已經指出使用'Seq'來避免臨時數組。無論如何,你是否也試過@塔米爾的第一個解決方案?我有興趣知道如果避免多個'Seq's的開銷有什麼區別。 –

+0

Tarmil的數組表達式的解決方案比上面的解決方案2稍微慢一些,時間爲0.9s。 – Mau

相關問題