Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F#用有趣的东西替换ref变量_F#_Refactoring_Functional Programming - Fatal编程技术网

F#用有趣的东西替换ref变量

F#用有趣的东西替换ref变量,f#,refactoring,functional-programming,F#,Refactoring,Functional Programming,我有下面的F#函数,它利用一个ref变量来播种和跟踪一个运行的总数,有些东西告诉我这不符合fp的精神,甚至它本身也不是特别清晰的。我想要一些关于最清晰(可能的fp,但如果命令式方法更清晰,我愿意接受)的方向,用F#来表达这一点。请注意,selectItem实现了随机加权选择算法 type WeightedItem(id: int, weight: int) = member self.id = id member self.weight = weight let selectI

我有下面的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())

嗯,这里有一种方法是使用
折叠
,但它感觉不雅,而且总是遍历整个列表

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()) 

嗯,这是一个带有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()) 

嗯,这里有一些可变项和一个循环;但是仍然遍历整个列表

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()) 
键入WeightedItem(id:int,weight:int)=
成员self.id=id
构件自重=重量
让我们选择项(项:WeightedItem列表)(rand:System.Random)=
让totalWeight=List.sumBy(乐趣(项目:WeightedItem)->item.weight)项目
让所选内容=随机数下一个(总重量)+1
设可变运行权重=0
让mutable找到=无
对于项目中的项目,请执行以下操作:
匹配
|无->
运行重量=选择
找到()
发现。价值
let items=[new WeightedItem(1100)
新权重编辑项(2,50)
新权重editem(3,25)]
let selection=selectItems项目(new System.Random())

这是三个中我最喜欢的。我期待着F#添加
break
的那一天。当然,您可以调用
GetEnumerator
并获得完全控制权,但这也很难看。

这里是一个使用递归函数的搜索算法版本。我的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

就效率而言,Igor的答案可能是存储在列表中的项目的最佳答案,但由于Brian的扫描方法代表了一种重复序列操作模式,因此我建议使用更紧凑的变体:

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

使用
Seq.unfold
构建累积
运行重量的按需序列,然后使用
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

谢谢,我想折叠可能也很合适(播种和积累!),但唉,我不能忍受浏览整个列表。哇!是的,那有点多。但我非常感激看到一个替代方法,因为它本身!是的,我在玩for/in,但没有找到休息的地方。我开始认为我的原始实现离基础不远。事实上,一个局部尾部递归函数在这里可以很好地工作。很好。这正是我想要的。谢谢Jon,我学到了很多新东西来解决这个问题。但是为了让类型推断工作并满足编译器的要求,我需要做(0,items)|>Seq.unfold((gen body在此))|>Seq.pick idAh,这是因为您将
weightedItem
定义为一个类,因此F#无法推断
项的类型。weight
。在我的测试中,我将其定义为记录类型
typeweightedItem={id:int;weight:int}
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