F # - не понимает состав функции (трансформируйте функцию get files, чтобы получить функцию дублирования файлов) - PullRequest
2 голосов
/ 14 декабря 2011

Вот моя проблема ... Я не понимаю, почему это не работает для меня:)

Если говорить более конкретно, у меня есть функция get files (не проблема, но обратная связь приветствуется):

type DirectoryOptions = Directory of string * Option<SearchOption>

type SearchOptions =
    | SearchSubDirectories
    | SearchCurrentDirectory

let WithExtensionIn extlist filename =
    let fileext = Path.GetExtension filename
    extlist |> Seq.exists (fun e -> e = fileext)

let GetFiles dir extlist =
    match dir with
        | Some diroptions ->
            let directoryname, suboptions = diroptions
            match suboptions with
                | Some SearchSubDirectories | None ->
                    Directory.GetFiles(directoryname, "*.*", SearchOption.AllDirectories)
                    |> Seq.filter (WithExtensionIn extlist)
                | Some SearchCurrentDirectory ->
                    Directory.GetFiles(directoryname, "*.*", SearchOption.TopDirectoryOnly)
                    |> Seq.filter (WithExtensionIn extlist)
        | None ->
            Directory.GetFiles(Directory.GetCurrentDirectory(), "*.*", SearchOption.AllDirectories)
                    |> Seq.filter (WithExtensionIn extlist)

Я хочу объединить это в функцию «Получить дубликаты файлов».Возможно, я не смогу, но я пытаюсь погрузиться в функциональное мышление.Моя текущая попытка, которая, исходя из моего понимания, должна работать, не работает.Это означает, что мое понимание неверно, и я хотел бы получить помощь / разъяснение о том, как решить эту проблему.Насколько я понимаю, в композиции функций самая внутренняя функция может иметь n входных параметров, но только один выход, а остальные функции, которые обертывают ее, могут иметь только один вход и один выход.Я не совсем уверен, как интерпретируется первая функция (возможно, это плохое слово) в контексте композиции в F #, потому что нет четкого ввода / вывода.Я считаю, что это прямое влияние карри.

Вот моя текущая попытка:

let GetDuplicateFiles =
    let LengthAndExtension file =
        //this is faked for simplicity
        (12, ".htm")

    let GroupSizeGreaterThanOne group =
        let _, values = group
        Seq.length values > 1

    let content file =
        //again faked
        ()

    let groups items =
        snd items

    GetFiles
    >> Seq.groupBy LengthAndExtension
    >> Seq.filter GroupSizeGreaterThanOne
    >> Seq.collect groups
    >> Seq.groupBy content
    >> Seq.filter GroupSizeGreaterThanOne
    >> Seq.collect groups

Это дает мне ошибку компиляции на Seq.groupBy LengthAndExtension, ошибка типа '' b-> seq 'несовместим с типом' seq <'a>'

Любые мысли / отзывы приветствуются.Я думаю, что я ищу момент ах ха, если вы знаете, что я имею в виду

Ответы [ 2 ]

2 голосов
/ 14 декабря 2011

Форвард-композиция (>>) создает новую функцию, передавая выходные данные первого как входные данные второму.

Подпись выявляет проблему: ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3

Он принимает две функции, каждая из которых принимает один аргумент. Но GetFiles принимает два аргумента. Быстрое решение - заменить GetFiles на кортеж: let GetFiles (dir, extlist) = ....

1 голос
/ 14 декабря 2011

Используя состав функции >>, имеем: (f >> g) x = g(f(x)).Это означает, что мы сначала применяем f ко входу, а после этого g.

Простым способом использования композиции функций является написание функции сначала с использованием конвейера, а затем рефакторинг.Я обычно начинаю с формы x |> f1 |> f2 |> ... |> fn, удаляю ввод x и заменяю конвейер (|>) на состав функции (>>), чтобы иметь правильную составную функцию f1 >> f2 >> ... >> fn.Однако в вашем примере это не очевидно:

let GetDuplicateFiles(dir, extlist) =
    //...
    GetFiles dir extlist
    |> Seq.groupBy LengthAndExtension
    |> Seq.filter GroupSizeGreaterThanOne
    |> Seq.collect groups
    |> Seq.groupBy content
    |> Seq.filter GroupSizeGreaterThanOne
    |> Seq.collect groups

Если вы измените GetFiles с каррированной формы GetFiles dir extlist на форму кортежа GetFiles(dir, extlist), вы можете легко применить вышеописанный трюк, чтобы использоватьсостав функции.

А на заметке можно изменить Seq.filter ... >> Seq.collect на Seq.choose ... >> Seq.concat, чтобы сохранить один раз в последовательности:

let GetDuplicateFiles =
    //...

// (dir, extlist)
// |>
    GetFiles
    >> Seq.groupBy LengthAndExtension
    >> Seq.choose GroupSizeGreaterThanOne
    >> Seq.concat
    >> Seq.groupBy content
    >> Seq.choose GroupSizeGreaterThanOne
    >> Seq.concat

Функция GroupSizeGreaterThanOne немногоизменено:

let GroupSizeGreaterThanOne (_, values) =
    if Seq.length values > 1 then Some values else None
...