我是功能世界的新手,并感谢对此的帮助。
我想从这个简单的函数中取代丑陋的命令式代码,但不知道该怎么做。
我想要的是根据概率值从 IEnumerable(F# 中的 seq)中随机选择一些元素 - 元组中的第二个项目(因此“概率”为 0.7 的项目将比 0.1 的项目更频繁地被选择)。
/// seq<string * float>
let probabilitySeq = seq [ ("a", 0.7); ("b", 0.6); ("c", 0.5); ("d", 0.1) ]
/// seq<'a * float> -> 'a
let randomPick probSeq =
let sum = Seq.fold (fun s dir -> s + snd dir) 0.0 probSeq
let random = (new Random()).NextDouble() * sum
// vvvvvv UGLY vvvvvv
let mutable count = random
let mutable ret = fst (Seq.hd probSeq )
let mutable found = false
for item in probSeq do
count <- count - snd item
if (not found && (count < 0.0)) then
ret <- fst item //return ret; //in C#
found <- true
// ^^^^^^ UGLY ^^^^^^
ret
////////// at FSI: //////////
> randomPick probabilitySeq;;
val it : string = "a"
> randomPick probabilitySeq;;
val it : string = "c"
> randomPick probabilitySeq;;
val it : string = "a"
> randomPick probabilitySeq;;
val it : string = "b"
我认为
randomPick
的命令式实现非常简单,但在功能上呢?这是功能性的,但采用 列表 而不是 seq (想要)。
//('a * float) list -> 'a
let randomPick probList =
let sum = Seq.fold (fun s dir -> s + snd dir) 0.0 probList
let random = (new Random()).NextDouble() * sum
let rec pick_aux p list =
match p, list with
| gt, h::t when gt >= snd h -> pick_aux (p - snd h) t
| lt, h::t when lt < snd h -> fst h
| _, _ -> failwith "Some error"
pick_aux random probList
最佳答案
使用 Matajon 建议的原理的 F# 解决方案:
let randomPick probList =
let ps = Seq.skip 1 (Seq.scan (+) 0.0 (Seq.map snd probList))
let random = (new Random()).NextDouble() * (Seq.fold (fun acc e -> e) 0.0 ps)
Seq.find (fun (p, e) -> p >= random)
(Seq.zip ps (Seq.map fst probList))
|> snd
但是在这种情况下我可能也会使用基于列表的方法,因为无论如何都需要预先计算概率值的总和......
关于f# - 如何从函数中删除命令式代码?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1586188/