Напишите точку выражения последовательности f # бесплатно - PullRequest
0 голосов
/ 22 февраля 2012

Я учусь писать бесплатно, и все шло отлично, пока я не наткнулся на это:

let rec toSeq (reader : SqlDataReader) toItem = seq {
        if reader.Read()
        then
            yield toItem reader
            yield! toSeq reader toItem
        else
            yield! Seq.empty }

и это:

let execute sql init toItem = 
    seq {
        use command = new SqlCommand(sql)
        command |> init
        use connection = new SqlConnection("")
        connection.Open()
        command.Connection <- connection
        use reader = command.ExecuteReader()
        yield! toSeq reader toItem } |> Seq.toList

Я понятия не имею, как пройтипостроитель последовательностей ... Это вообще возможно?

Мне нужно убедиться, что использование по-прежнему работает корректно.

К вашему сведению: я знаю, что здесь бессмысленно использовать бессмысленное программирование,Просто поймите, это упражнение для меня.

ОБНОВЛЕНИЕ: Вот моя первая попытка по второй функции.Мне пришлось удалить ссылки на последовательность, хотя:

let ExecuteReader (command : SqlCommand) (connection : SqlConnection) = 
    command.Connection <- connection
    command.ExecuteReader()

let c x y =  ((>>) x) << ((<<) << y)

let (>>|) = c

let execute =
    ExecuteReader 
    >>| ((>>) toSeq) (flip using) 
    >>| using 
    >>| using

1 Ответ

2 голосов
/ 22 февраля 2012

Ну, как уже отмечалось в комментариях, написание императивного кода в стиле без точек вообще не очень хорошая идея.Он не только не делает его более читабельным , но и делает его более подверженным ошибкам , поскольку его выполнение более сложно рассуждать.Даже с функциональным кодом я часто нахожу, что стиль без точек гораздо более читабелен (и не намного дольше).

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

Код для первой функции должен быть похож на этот (это будет менее эффективно , потому что выражения последовательности оптимизированы):

let rec toSeq (reader : SqlDataReader) toItem = Seq.delay (fun () ->
  if reader.Read() then 
    Seq.concat [ Seq.singleton (toItem  reader); toSeq reader toItem ]
  else 
    Seq.empty)

Evenв стиле без точек вам все еще нужен Seq.delay, чтобы убедиться, что вы выполняете последовательность лениво.Однако вы можете определить немного другую функцию, которая позволит вам писать код в более бессмысленном стиле.

// Returns a delayed sequence generated by passing inputs to 'f'
let delayArgs f args = Seq.delay (fun () -> f args)

let rec toSeq2 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (fun (reader, toItem) ->
    if reader.Read() then 
      Seq.concat [ Seq.singleton (toItem  reader); toSeq2 (reader, toItem) ]
    else 
      Seq.empty)

Теперь тело функции - это просто некоторая функция, переданная функции delayArgs.Мы можем попробовать составить эту функцию из других функций в стиле без точек.Хотя if сложно, поэтому мы заменим его комбинатором, который принимает три функции (и передает всем один и тот же вход):

let cond c t f inp = if c inp then t inp else f inp

let rec toSeq3 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fun (reader, _) -> reader.Read()) 
                  (fun (reader, toItem) -> 
                     Seq.concat [ Seq.singleton (toItem  reader); 
                                  toSeq3 (reader, toItem) ])
                  (fun _ -> Seq.empty))

Вы не можете обрабатывать вызовы членов в бессмысленной точке.стиль, поэтому вам нужно определить функцию, которая вызывает Read.Затем вы также можете использовать функцию, которая возвращает постоянную функцию (чтобы избежать коллизий имен, с именем konst):

let read (reader:SqlDataReader) = reader.Read()
let konst v _ = v

Используя два, вы можете превратить последний и второй аргумент в бессмысленныйстиль:

let rec toSeq4 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fst >> read) 
                  (fun (reader, toItem) -> 
                     Seq.concat [ Seq.singleton (toItem  reader); 
                                  toSeq4 (reader, toItem) ])
                  (konst Seq.empty))

Использование более сумасшедших комбинаторов (uncurry Существуют в Haskell; комбинатор list2 также может быть написан в стиле без очков, но я думаю, вы поняли идею):

let list2 f g inp = List.Cons(f inp, List.Cons(g inp, []))
let uncurry f (a, b) = f a b

let rec toSeq5 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fst >> read) 
                  (list2 ((uncurry (|>)) >> Seq.singleton) toSeq5 >> Seq.concat)
                  (konst Seq.empty))

Это не совсем компилируется, потому что toSeq5 оценивается как часть его определения, но если вы включили некоторую функцию задержки, она может фактически сделать то же самое, что и изначально.

Резюме - Я больше не знаю, является ли приведенный выше код правильным и как он оценивает (это может с нетерпением оценить читателя или содержать какую-то другую ошибку).Он выполняет проверку типов, так что, вероятно, он не слишком далек от работы.Код полностью нечитаем, трудно отлаживать и невозможно изменить.

Думайте об этом как о крайнем примере сумасшедшего кода без точек, который вы можете написать на F #.На практике я думаю, что стиль без точек должен использоваться только для таких тривиальных вещей, как составление функций с использованием >>.Если вам нужно определить комбинаторы, такие как uncurry или konst, людям будет действительно трудно читать ваш код.

...