2

如何在 F# 中实现并行过滤?基本上我想Array.Parallel.Choose用序列表达式创建一个数组。

我试过:

Async.Parallel [for x in 1..40 do yield async { if x % 5 = 0 then return x }]

但这是类型不匹配,因为 if 并不总是有值(即Async<unit>不是Async<'a>)。

我正在迭代一大组数字(1,000,000,000 +),所以我不想预先生成序列。

if 语句中的实际检查是:

let isPalindrome (x : int) = let numberArray = x.ToString().ToCharArray()
                             numberArray = Array.rev numberArray

尝试使用 PSeq:

[for x in 990000..999999 do for y in 990000..999999 do yield (x, y, x*y)]
|> PSeq.filter(fun (x, y, z) -> isPalindrome z)
|> Seq.max

这导致OutOfMemoryException.

丑陋的解决方法:

let numberArray = [|990000..999999|]
let result = numberArray |> Array.Parallel.collect(fun x -> [| for y in numberArray do if isPalindrome (x*y) then yield (x, y, x*y)|])
             |> Array.maxBy(fun (x, y, z) -> z)
4

2 回答 2

1
let isPalindrome (x : int) = let numberArray = x.ToString().ToCharArray()
                             numberArray = Array.rev numberArray

我想出了如何使用该PSeq库对序列进行延迟过滤:

let generator =
    seq {
        for x in 990000..999999 do 
            for y in 990000..999999 do 
                yield (x, y, x*y) 
    }

generator
|> PSeq.filter(fun (x, y, z) -> isPalindrome z)
|> Seq.max

真实:00:00:28.938,CPU:00:01:49.078,GC gen0:3448,gen1:2,gen2:0

可悲的是,这比我丑陋的黑客要慢:

let numberArray = [|990000..999999|]
let result = numberArray |> Array.Parallel.collect(fun x -> [| for y in numberArray do if isPalindrome (x*y) then yield (x, y, x*y)|])
             |> Array.maxBy(fun (x, y, z) -> z)

真实:00:00:24.779,CPU:00:01:32.109,GC gen0:2680,gen1:18,gen2:1

于 2013-04-19T21:37:13.163 回答
0

基于 Array.Choose 实现的更高效的解决方案:

 open System.Collections.Concurrent
 open System.Threading.Tasks
 open System


 let parallelFilter f (array: 'T[]) = 

            let inputLength = array.Length                      

            let isChosen : bool [] = Array.zeroCreate inputLength                
            let mutable outputLength = 0        
            let range = Partitioner.Create(0,inputLength)
            Parallel.ForEach(
                         range,
                         (fun () ->0),
                         (fun (start,finish) _ count -> 
                            let mutable localCount = 0
                            for i in start .. finish-1 do
                                match f array.[i] with 
                                | true -> () 
                                | false -> 
                                    isChosen.[i] <- true                                      
                                    localCount <- localCount+1
                            localCount),
                          Action<int> (fun x -> System.Threading.Interlocked.Add(&outputLength,x) |> ignore )
                          ) |> ignore         

            let output = Array.zeroCreate outputLength
            let mutable curr = 0
            for i = 0 to isChosen.Length-1 do 
                if isChosen.[i] then 
                    output.[curr] <- output.[i]
                    curr <- curr + 1
            output
于 2017-04-05T20:15:46.347 回答