我怎么做F#在APL中称为压缩?

时间:2014-03-30 09:17:41

标签: f# apl

在APL中,可以使用位向量来选择另一个向量的元素;这叫做压缩。例如,1 0 1/3 5 7将产生3 7。

在函数式编程中,特别是F#中是否存在可接受的术语?

这是我的F#程序:

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

[<EntryPoint>]
let main argv = 

    0 // return an integer exit code

我想要做的是计算一个新的字符串[],它将是[|&#34; Bob&#34 ;;苏&#34; |]

如何在F#中做到这一点?

4 个答案:

答案 0 :(得分: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。如另一个答案中所述,将Array替换为Seq以避免构造中间数组。

我希望您会发现F#中缺少许多APL原语。对于列表和序列,可以通过将SeqArrayList模块中的基元串联起来构建许多模块,如上所述。供Seq模块的here is an overview参考。

答案 1 :(得分: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以避免构建中间数组,但它也是正确的。

答案 2 :(得分:1)

有人可能会说这更惯用:

Seq.map2 (fun l1 l2 -> if l2 = 1 then Some(l1) else None) list1 list2
|> Seq.choose id
|> Seq.toArray
编辑(管道爱好者)

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

答案 3 :(得分:0)

SørenDebois的解决方案很好,但正如他指出的那样,但我们可以做得更好。让我们根据Søren的代码定义一个函数:

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.8秒)。

正如ildjarn评论的那样,filter的函数参数可以重写为snd >> (=) 1,尽管这会导致性能略有下降(<10%),这可能是因为生成了额外的函数调用