代码之家  ›  专栏  ›  技术社区  ›  akaphenom

f如何将函数变量传递给成员函数

f#
  •  2
  • akaphenom  · 技术社区  · 16 年前

    对于具有muiltiple字段的类型(候选者),可以得分(这里有一个带有_scorexx的具体示例),并计算总百分比分数:

    type ScorableCandidate() =
        let mutable _scoreXXX: int  =  0 ;
        let mutable _percentXXX: float = 0. ;
    
        member this.scoreXXX
            with get() = _scoreXXX
            and  set(v) =
                _scoreXXX<- v
                propertyChanged.Trigger(this, new PropertyChangedEventArgs("scoreXXX"))
    
        member this.percentXXX
            with get() = _percentXXX
            and  set(v) =
                _percentXXX <- v
                propertyChanged.Trigger(this, new PropertyChangedEventArgs("percentXXX"))
    

    我想把百分比的计算分开,这样我就不需要每次都重新定义函数。不幸的是,我对如何使用成员函数进行此操作迷路了。我正在抽象外观的代码模板是:

    let scoreXXXs () =
         let totalXXXScore  = List.fold (fun acc (candidate: ScorableCandidate) -> acc + candidate.scoreXXXs) 0 brokers
    
         let score (candidate: ScorableCandidate) =
                     candidate.percentXXXs <- (float candidate.scoreXXXs) / float totalXXXScore   * 100.
    
         if totalXXXScore  > 0 then
                    List.iter score <| brokers
    

    我想我可以将函数定义为“score”,并传递给适当的成员访问器。这样我就可以写作了

        score XXX  // where xxx is some property in the class that needs to be scored
        score YYY  // where yyy is some property in the class that needs to be scored
    

    有人认为我是通过传递函数来获取访问权限的,这对于getter来说已经足够简单了,但我似乎无法找到setter。我意识到我可以推迟思考——但我觉得这可能不是最好的方式……我现在在:

        let scoreField (accessF : (ScorableCandidate -> int)) (setF : (float -> unit)) =
            let totalScore = List.fold (fun acc (candidate: ScorableCandidate) -> acc + accessF candidate) 0 brokers
    
            let score (candidate: ScorableCandidate) =
                setF <| (accessF candidate |> float) / float totalScore  * 100.
    
            if totalScore > 0 then
                List.iter score <| brokers
    
        scoreField (fun c -> c.scoreXXX) (fun f -> ())
    

    但是我不知道如何(或者如果可能)将setter表示为类型上的lambda函数(在这里我可以将instace作为parateter传递给lambda函数并以某种方式调用它)。

    思想?谢谢。

    更新发现此方法(想法): http://laurent.le-brun.eu/site/index.php/2009/10/17/52-dynamic-lookup-operator-aka-duck-typing-in-fsharp

    3 回复  |  直到 16 年前
        1
  •  2
  •   Pavel Minaev    16 年前

    你在正确的轨道上,但是你的设置器丢失了你要设置的对象。所以类型 setF 应该是:

    setF : (ScorableCandidate -> float -> unit)
    

    所以你可以这样使用它:

    let score (candidate: ScorableCandidate) =
        (setF candidate) <| (accessF candidate |> float) / float totalScore  * 100.
    

    打电话给你 scoreField 因此:

    scoreField (fun c -> c.scoreXXX) (fun c f -> c.percentXXX <- f)
    
        2
  •  2
  •   Tomas Petricek    16 年前

    所以,如果我理解正确的话,你需要在一个候选人中存储几个分数(对于不同的事情),然后对这些分数进行计算。

    在这种情况下,我会考虑 Score 一个单独的类型,候选人会使用-然后你可以很容易地添加多个分数。如果您需要将分数和百分比公开为候选人的直接属性,请使用 IPropertyChange ,那么您应该能够编写如下内容:

    /// Takes a name of this score item (e.g. XXX) and a 
    /// function to trigger the property changed event
    type Score(name:string, triggerChanged) = 
      let mutable score = 0
      let mutable percent = 0.0
      member x.Score 
        with get() = score 
        and set(v) = 
          score <- v
          triggerChanged("Score" + name)
      member x.Percent 
        with get() = percent 
        and set(v) = 
          percent <- v
          triggerChanged("Percent" + name)
    

    现在您可以简单地使用 分数 在候选对象中多次需要对象(因为我们还希望公开直接属性,所以有一些样板文件,但应该是合理的数量):

    type ScorableCandidate() as this = 
      // Create trigger function & pass it to Score objects
      let trigger n = propertyChanged.Trigger(this, n)
      let infoXxx = new Score("Xxx", trigger)
      member this.InfoXxx = scoreXxx             // Exposes the whole Score object
      member this.ScoreXxx = scoreXxx.Score      // & individual...
      member this.PercentXxx = scoreXxx.Percent  // ...properties
    

    现在,参数化函数可以简单地采用 ScorableCandidate 返回一个 分数 对象:

    let scores f =     
      let totalScore  = List.fold (fun acc (candidate: ScorableCandidate) -> 
        let so = f candidate // Get 'Score' object
        acc + so.Score) 0 brokers     
     let score (candidate: ScorableCandidate) =     
       let so = f candidate // Get 'Score' object
       so.Percent <- (float so.Score) / float totalScore * 100.0
     if totalScore > 0 then     
       List.iter score <| brokers    
    

    示例调用很简单:

    scores (fun (c:ScorableCandidate) -> c.InfoXxx)
    

    这就给 scores 功能尽可能简单,而且它也是一个可扩展的解决方案,可以方便地将其他计算添加到 分数 对象。缺点(例如,与Pavel的直接解决方案相比)是设计 分数 类型(但是,在 可评分候选人 如果只需要在类中直接公开可读属性,那么可以说更容易——我认为这就足够了)。

        3
  •  0
  •   kvb    16 年前

    为了简化API,您可能需要考虑以下选项之一:

    1. 使用反射。那你就可以了 scoreField "XXX" 您的方法可以将“xxx”显式转换为 MethodInfo 为你的 get_scoreXXX 和 set_percentXXX 方法。这有一个缺点,即它在编译时不检查方法名,并且它有伴随反射的性能损失。

    2. 使用报价。那你就可以了 scoreField <@ fun c -> c.scoreXXX, c.percentXXX @> . 否则,这将类似于反射示例,除非您有更多的编译时检查。

    3. 创建表示分数/百分比组合的类型,并创建该类型的属性,而不是为每个属性单独创建属性。你的 ScorePercent 类可以为得分和百分比设置getter和setter(并提高自己的更改通知)。那么,你可以 scoreField (fun c -> c.XXX) 那里 XXX 成员属于类型 记分率 .