【发布时间】:2010-12-07 20:32:47
【问题描述】:
我是函数式世界的新手,非常感谢这方面的帮助。
我想从这个简单的函数中SUPERCEDE丑陋的命令式代码,但不知道该怎么做。
我想要的是根据概率值从 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 很容易以命令方式实现,但在功能上?
这是功能性的,但需要 list 而不是 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
【问题讨论】:
-
我也会选择基于列表的版本(您需要评估整个输入,因此 seq 的按需产量优势丧失了)。您可以修改第二个代码示例以将序列作为输入,并将该序列同时转换为列表和总和。然后将列表用于算法的基于列表的部分。该列表将以相反的顺序排列,但这对于计算无关紧要。代码: let sum,L = probabilitySeq |> Seq.fold (fun (sum,L) dir -> sum + snd dir, dir::L) (0.0,[]).
标签: f# functional-programming imperative-programming