2010-08-30 10 views
2

Etant donné un type (candidat) qui a des champs muiltiple on peut marquer (ici un exemple concret avec _scoreXXX) et calculer le pourcentage total score:F # comment passer une variable de fonction à une fonction de membre

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

I Je voudrais abscter le calcul des pourcentages, donc je n'ai pas besoin de redéfinir la fonction à chaque fois. Malheureusement, je suis perdu sur la façon de le faire avec les fonctions membres. Le modèle du code que je regarde est abstraction:

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 

Je suppose que je voudrais être en mesure de définir la fonction comme « score » et de passer dans les accesseurs membres de aapropriate. Un tel que je pourrais écrire

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 

Une pensée que j'avais passait des fonctions pour obtenir l'accès, et qui est assez facile à faire pour le getter, mais je ne peux pas sembler figurer le compositeur. Je me rends compte que je pouvais reporter à la réflexion pour cela - mais je pense que peut-être pas le meilleur (f #) façon ... À ce moment, je suis à:

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

Mais je ne sais pas comment (ou si il est possible de représenter le setter comme une fonction lambda sur le type (où peut-être je peux passer l'instace en tant que paraneter à la fonction lambda et l'invoquer en quelque sorte).

Pensées? Merci d'avance.

Mise à jour Trouvé cette approche (pensées): http://laurent.le-brun.eu/site/index.php/2009/10/17/52-dynamic-lookup-operator-aka-duck-typing-in-fsharp

Répondre

2

Vous êtes sur la bonne voie, mais l'objet sur lequel vous allez définir le champ est absent.Ainsi, le type de setF devrait être:

setF : (ScorableCandidate -> float -> unit) 

donc alors vous l'utiliser comme:

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

et appelez votre scoreField ainsi:

scoreField (fun c -> c.scoreXXX) (fun c f -> c.percentXXX <- f) 
+0

choisir cette solution - parce que je ne fais que développer un prototype, et cela a fonctionné. Je pense que ce sont toutes des approches valables, et comme Tomas l'a mentionné une nouvelle conception de la couche d'objet pourrait être un meilleur plan, mais je suis contraint par une couche RPC existante qui empêche cela de mon côté. Merci a tous – akaphenom

0

Pour rendre l'API plus simple, vous voudrez peut-être envisager l'une des options suivantes:

  1. réflexion utilisation. Ensuite, vous pouvez faire scoreField "XXX", et votre méthode pourrait traduire explicitement "XXX" en MethodInfo s pour vos méthodes get_scoreXXX et set_percentXXX. Cela présente l'inconvénient de ne pas vérifier les noms de méthodes au moment de la compilation et d'avoir la pénalité de performance qui accompagne la réflexion.

  2. Utilisez des citations. Ensuite, vous pouvez faire scoreField <@ fun c -> c.scoreXXX, c.percentXXX @>. Cela fonctionnerait sinon de la même manière que l'exemple de réflexion, sauf que vous auriez un peu plus de temps de compilation.

  3. Créez un type qui représente les combinaisons score/pourcentage et créez des propriétés de ce type plutôt que des propriétés distinctes pour chacun. Votre classe ScorePercent pourrait avoir des getters et setters pour le score et le pourcentage (et augmenter ses propres notifications de changement). Ensuite, vous pouvez faire scoreField (fun c -> c.XXX), où le membre XXX est de type ScorePercent.

+0

Peut-être que je n'étais pas clair, j'ai pris un moment pour décider comment formuler la question, mes applogies si j'étais trompeuse. Je veux pouvoir paramétrer ceci afin que scoreXXX soit juste l'une des options - que la logique là (pour scoring) puisse être étalée fonctionner sur plusieurs propriétés de la classe sans avoir à le coder spécifiquement. scoreXXX ou scoreYYY ou scoreZZZ ou scoreXYZ etc sont tous des memebers différents que je souhaite exécuter sur ... – akaphenom

+0

@akaphenom - J'ai édité ma réponse pour vous donner d'autres options. – kvb

+0

Merci. Quels seraient les avantages de l'utilisation de Citations par rapport à la solution fournie par Pavel Minaev? – akaphenom

2

Donc, si je comprends correctement, vous devez stocker plusieurs scores (pour différentes choses) dans un seul candidat et ensuite effectuer des calculs sur ces scores.

Dans ce cas, je considérerais Score comme un type distinct qui serait utilisé par le candidat - vous pouvez alors facilement ajouter plusieurs scores. Si vous avez besoin d'exposer marquer & pour cent en tant que propriétés directes du candidat & notify à l'aide IPropertyChange, alors vous devriez être en mesure d'écrire quelque chose comme ceci:

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

Maintenant, vous pouvez simplement utiliser l'objet Score autant de fois vous avez besoin le candidat (parce que nous voulons aussi exposer les propriétés directes, il y a un passe-partout, mais il devrait être raisonnable):

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 

maintenant, votre fonction paramétrés peut simplement prendre une fonction qui prend ScorableCandidate et renvoie une Score objet:

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  

Exemple d'appel serait simplement:

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

Cela rend l'appel à la fonction scores aussi facile qu'il peut obtenir et il est également une solution évolutive qui le rend facile d'ajouter d'autres calculs à l'objet Score. L'inconvénient (par exemple par rapport à la solution simple de Pavel) est qu'il faut travailler un peu plus sur la création du type Score (cependant, déclarer de nouveaux scores dans ScorableCandidate est sans doute plus facile si vous avez seulement besoin d'exposer la propriété lisible directement dans la classe - Je pense que ça devrait suffire).

Questions connexes