给定一个具有多个字段的类型(候选者)可以得分(这里是一个具体的例子,用_scoreXXX)并计算总百分比得分:
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 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。我意识到我可以推迟对此进行反思 - 但我觉得这可能不是最好的(f#)方式......此时我处于:
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函数(也许我可以将实例作为paraneter传递给lambda函数并以某种方式调用它。)
思考?谢谢。
更新发现这种方法(想法): http://laurent.le-brun.eu/site/index.php/2009/10/17/52-dynamic-lookup-operator-aka-duck-typing-in-fsharp
答案 0 :(得分:2)
你在正确的轨道上,但是你的定位器缺少你要设置该字段的对象。所以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)
答案 1 :(得分:2)
所以,如果我理解正确,你需要在一个候选人中存储几个分数(针对各种不同的东西),然后对这些分数进行计算。
在这种情况下,我会考虑将Score
作为候选人使用的单独类型 - 然后您可以轻松添加多个分数。如果你需要公开得分&amp;作为候选人的直接属性的百分比&amp;使用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)
现在你可以在候选者中多次使用Score
对象(因为我们也希望公开直接属性,有一些样板,但它应该是合理的数量):
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
并返回Score
对象的函数:
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
函数的调用尽可能简单,并且它也是可扩展的解决方案,可以轻松地将其他计算添加到Score
对象。缺点(例如,与Pavel的直接解决方案相比)是设计Score
类型还有一些工作(但是,如果您只需要公开,则在ScorableCandidate
中声明新分数可能会更容易直接在类中的可读属性 - 我认为应该足够了。)
答案 2 :(得分:0)
为了简化API,您可能需要考虑以下选项之一:
使用反射。然后,您可以执行scoreField "XXX"
,并且您的方法可以为您的MethodInfo
和get_scoreXXX
方法明确地将“XXX”翻译为set_percentXXX
s。这样做的缺点是它不会在编译时检查方法名称,并且它会带来反射带来的性能损失。
使用引文。然后你可以做scoreField <@ fun c -> c.scoreXXX, c.percentXXX @>
。否则,这将与反射示例类似,除了您需要更多的编译时检查。
创建一个表示分数/百分比组合的类型,并创建该类型的属性,而不是为每个类型创建单独的属性。您的ScorePercent
课程可能有得分和百分比的getter和setter(并提出自己的更改通知)。然后,您可以执行scoreField (fun c -> c.XXX)
,其中XXX
成员的类型为ScorePercent
。