我有以下F#函数,它使用ref变量来播种并跟踪运行总计,有些事情告诉我这不是fp的精神,甚至不是特别清楚。我想在最清楚的方面(可能的fp,但如果一个必要的方法更清楚,我会对此开放)的方向在F#中表达这个方向。请注意,selectItem实现了随机加权选择算法。
type WeightedItem(id: int, weight: int) =
member self.id = id
member self.weight = weight
let selectItem (items: WeightedItem list) (rand:System.Random) =
let totalWeight = List.sumBy (fun (item: WeightedItem) -> item.weight) items
let selection = rand.Next(totalWeight) + 1
let runningWeight = ref 0
List.find
(fun (item: WeightedItem) ->
runningWeight := !runningWeight + item.weight
!runningWeight >= selection)
items
let items = [new WeightedItem(1,100); new WeightedItem(2,50); new WeightedItem(3,25)]
let selection = selectItem items (new System.Random())
答案 0 :(得分:4)
这是使用递归函数的搜索算法的一个版本。我的F#非常生疏,当我们找不到任何东西时我不知道该返回什么:
let rec find list item total =
match list with
| h::t -> if h > total then h else find t item total+h
| [] -> 0 //<-- return some sort of default to say can't find the item
修改
完整代码:
type WeightedItem(id: int, weight: int) =
member self.id = id
member self.weight = weight
let selectItem (items: WeightedItem list) (rand:System.Random) =
let totalWeight = List.sumBy (fun (item: WeightedItem) -> item.weight) items
let selection = rand.Next(totalWeight) + 1
let rec find runningWeight ((h:WeightedItem)::t) =
let newRunningWeight = runningWeight + h.weight
if newRunningWeight >= selection then
h
else
find newRunningWeight t
find 0 items
let items = [new WeightedItem(1,100)
new WeightedItem(2,50)
new WeightedItem(3,25)]
let selection = selectItem items (new System.Random())
答案 1 :(得分:2)
嗯,这是Seq.scan的一个,但它也感觉非常难看......
type WeightedItem(id: int, weight: int) =
member self.id = id
member self.weight = weight
let selectItem (items: WeightedItem list) (rand:System.Random) =
let totalWeight = List.sumBy (fun (item: WeightedItem) -> item.weight) items
let selection = rand.Next(totalWeight) + 1
Seq.scan
(fun (runningWeight,found,itemO) (item: WeightedItem) ->
if not found then
let newRunningWeight = runningWeight + item.weight
newRunningWeight, newRunningWeight >= selection, Some(item)
else
(runningWeight,found,itemO))
(0,false,None)
items
|> Seq.find (fun (rw,f,i) -> f)
|> (fun (rw,f,i) -> i.Value)
let items = [new WeightedItem(1,100)
new WeightedItem(2,50)
new WeightedItem(3,25)]
let selection = selectItem items (new System.Random())
答案 2 :(得分:2)
let selectItem (items: WeightedItem list) (rand:System.Random) =
let totalWeight = List.sumBy (fun (item: WeightedItem) -> item.weight) items
let selection = rand.Next(totalWeight) + 1
items
|> Seq.scan (fun acc (item : WeightedItem) -> acc + item.weight) 0
|> Seq.skip 1 |> Seq.zip items
|> Seq.find (fun (i, rw) -> rw >= selection) |> fst
答案 3 :(得分:2)
使用Seq.unfold
构建一个按需序列,累积runningWeight
,然后使用runningWeight
搜索具有足够大Seq.pick
的第一个元素:
let gen = function
| _, [] -> None
| runningWeight, item::items ->
let runningWeight = runningWeight + item.weight
Some((if runningWeight >= selection then Some item else None), (runningWeight, items))
Seq.unfold gen (0, xs) |> Seq.pick id
答案 4 :(得分:1)
嗯,这是使用fold
做到这一点的一种方法,但它感觉不够优雅,总是遍历整个列表...
type WeightedItem(id: int, weight: int) =
member self.id = id
member self.weight = weight
let selectItem (items: WeightedItem list) (rand:System.Random) =
let totalWeight = List.sumBy (fun (item: WeightedItem) -> item.weight) items
let selection = rand.Next(totalWeight) + 1
List.fold
(fun (runningWeight,found) (item: WeightedItem) ->
if not found then
let newRunningWeight = runningWeight + item.weight
newRunningWeight, newRunningWeight >= selection
else
(runningWeight,found))
(0,false)
items
|> fst
let items = [new WeightedItem(1,100)
new WeightedItem(2,50)
new WeightedItem(3,25)]
let selection = selectItem items (new System.Random())
答案 5 :(得分:1)
嗯,这里有一些变数和一个循环;仍然遍历整个列表...
type WeightedItem(id: int, weight: int) =
member self.id = id
member self.weight = weight
let selectItem (items: WeightedItem list) (rand:System.Random) =
let totalWeight = List.sumBy (fun (item: WeightedItem) -> item.weight) items
let selection = rand.Next(totalWeight) + 1
let mutable runningWeight = 0
let mutable found = None
for item in items do
match found with
| None ->
runningWeight <- runningWeight + item.weight
if runningWeight >= selection then
found <- Some(item)
| _ -> ()
found.Value
let items = [new WeightedItem(1,100)
new WeightedItem(2,50)
new WeightedItem(3,25)]
let selection = selectItem items (new System.Random())
这是我最喜欢的三个。我期待着F#添加break
的那一天。当然你可以打电话给GetEnumerator
并完全控制,但这也很难看。