F # как передать переменную функции в функцию-член - PullRequest
2 голосов
/ 30 августа 2010

Учитывая тип (Кандидат), который имеет несколько полей, можно набрать очки (здесь один конкретный пример с _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

Одна мысль, которую я имел, заключалась в передаче функций для получения доступа, и это достаточно легко сделать для получателя, но я не могу понять, что такое установщик. Я понимаю, что могу откладывать на размышления для этого - но я чувствую, что это может быть не самый лучший (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 -> ())

Но я не знаю, как (или если это возможно) представить сеттер как лямбда-функцию для типа (где, возможно, я могу передать instace в качестве паранетера лямбда-функции и как-нибудь ее вызвать).

Мысли? Спасибо вперед.

Обновление Нашел такой подход (мысли): http://laurent.le -brun.eu / сайта / index.php / 2009/10/17/52-динамический поиск, оператор-ака-уток-типирование-в-FSharp

Ответы [ 3 ]

2 голосов
/ 30 августа 2010

Вы на правильном пути, но ваш сеттер пропускает объект, на который вы собираетесь установить поле. Поэтому тип 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 голосов
/ 31 августа 2010

Итак, если я правильно понимаю, вам нужно сохранить несколько баллов (для разных вещей) в одном кандидате, а затем выполнить вычисления по этим баллам.

В этом случае я бы рассмотрел вопрос о том, чтобы сделать 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)

Теперь вы можете просто использовать объект 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.Недостаток (например, по сравнению с простым решением Павла) состоит в том, что есть немного больше работы по проектированию типа Score (однако объявление новых баллов в ScorableCandidate тогда, вероятно, проще, если вам нужно только выставить читаемое свойство непосредственно вкласс - который, я думаю, должно быть достаточно).

0 голосов
/ 30 августа 2010

Чтобы упростить API, вы можете рассмотреть один из следующих вариантов:

  1. Использовать отражение. Тогда вы могли бы сделать scoreField "XXX", и ваш метод мог бы явно перевести «XXX» в MethodInfo s для ваших get_scoreXXX и set_percentXXX методов. Недостаток этого метода в том, что он не проверяет имена методов во время компиляции и имеет снижение производительности, которое сопровождается отражением.

  2. Используйте цитаты. Тогда вы могли бы сделать scoreField <@ fun c -> c.scoreXXX, c.percentXXX @>. В противном случае это будет работать аналогично примеру отражения, за исключением того, что у вас будет немного больше проверки во время компиляции.

  3. Создайте тип, который представляет комбинацию счет / процент, и создайте свойства этого типа, а не отдельные свойства для каждого. Ваш класс ScorePercent может иметь методы получения и установки для оценки и процента (и поднимать свои собственные уведомления об изменениях). Затем вы можете сделать scoreField (fun c -> c.XXX), где член XXX имеет тип ScorePercent.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...