在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#中做到这一点?
答案 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原语。对于列表和序列,可以通过将Seq
,Array
或List
模块中的基元串联起来构建许多模块,如上所述。供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)
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%),这可能是因为生成了额外的函数调用