Абстрагирование цикла - PullRequest
       44

Абстрагирование цикла

2 голосов
/ 02 сентября 2011

Я иногда пишу циклы (рекурсивные функции) внутри заданий. Это делает для неудобного кода, как это:

let value =
  let rec loop a =
    if ... then a
    else loop a.B
  loop a

Я знаю, что мог бы переместить цикл за пределы привязки let, но его единственная цель - вычислить связанное значение.

Так что я подумал, что могу абстрагировать цикл в отдельную функцию:

let loop f a =
  let rec aux a =
    match f a with
    | Some b -> aux b
    | None -> a
  aux a

тогда я мог бы сделать:

let value = a |> loop (fun a -> if ... then None else Some a.B)

Может быть, это и лучше - по крайней мере, это похоже на присваивание, а не определение функции. Вот мои вопросы:

  1. Связывает ли рекурсивная функция в let что-то с запахом кода?
  2. Есть ли лучший способ реорганизовать это?
  3. Если нет, можно ли обобщить мою функцию loop или как-то улучшить?

Ответы [ 3 ]

4 голосов
/ 02 сентября 2011

Эти вопросы немного субъективны, но вот мои ответы:

  1. Нет
  2. Я думаю, что у тебя все в порядке.
  3. Вот как бы я это сделал:

    let rec loop guard step init =
        if guard init then init
        else loop guard step (step init)
    
    let value = a |> loop (fun a -> ...) (fun a -> a.B)
    
1 голос
/ 08 сентября 2011

Или вы можете определить y-комбинатор (я скопировал это определение из Как определить y-комбинатор без "let rec"? )

let rec y f x = f (y f) x

и сделать это:

let value = a |> y (fun loop a -> if ... then a else loop a.B)
0 голосов
/ 02 сентября 2011

Я думаю, что ваши решения или решения kvb совершенно хороши.

Я не знаю, какую структуру данных вы перебираете в цикле. Это похоже на структуру данных, поэтому может иметь смысл реализовать интерфейс IEnuemerable<'T> или написать функцию, которая превращает его в IEnumerable<'T>:

let rec asSeq a = seq {
  yield a 
  yield! asSeq a.B }

Тогда вы можете просто использовать Seq.find и дать ему необходимое условие:

// Using explicit conversion function
let value = a |> asSeq |> Seq.find (fun a -> ...)

// If 'a' actually implements 'seq<'T>', it is even nicer:
let value = a |> Seq.find (fun a -> ...)
...