Передача частично активных шаблонов в качестве аргументов?

Я изучаю F#, написав парсер рекурсивного спуска с использованием активных шаблонов.

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

В следующем примере показана проблема, с которой я столкнулся:

// Combines two patterns by chaining them.
let (|Chain|_|) (|Pattern1|_|) (* Should I use pipes here? *) (|Pattern2|_|) data =
    match data with
    |Pattern1 result ->
        match result with
        |Pattern2 result2 -> Some result2
        |_ -> None
    |_ -> None 

// Stupid test patterns
let (|IfBiggerThan10ThenDouble|_|) value = if value > 10 then Some (value*2) else None
let (|IfLessThan100ThenDouble|_ |) value = if value < 100 then Some (value*2) else None

match 20 with
// Do I need pipes here?
|Chain (IfBiggerThan10ThenDouble IfLessThan100ThenDouble) value -> printfn "%A" value // Should print 80
| _ -> printfn "Did not match"

Мое основное замешательство, кажется, о "|" оператор. Иногда кажется, что это часть типа шаблона, а иногда и часть имени.

1 ответ

Решение

Вам на самом деле не нужно реализовывать собственную цепочку шаблонов, потому что вы можете напрямую вкладывать шаблоны, что дает требуемый результат:

match 20 with
| IfBiggerThan10ThenDouble(IfLessThan100ThenDouble value) -> printfn "%A" value
| _ -> printfn "Did not match"

Это будет сначала называть IfBiggerThan10ThenDouble шаблон, который рассчитывает 20*2 и передает значение во вложенный шаблон IfLessThan100ThenDouble, Это снова удваивает значение и связывает его с value символ (когда это удается).

Тем не менее, ваша реализация Chain шаблон на самом деле работает и может быть назван так:

match 20 with
| Chain (|IfBiggerThan10ThenDouble|_|) (|IfLessThan100ThenDouble|_|) value -> 
    printfn "%A" value // Should print 80
| _ -> printfn "Did not match"

В общем, активная картина (|P|_|) на самом деле это просто функция со специальным именем. Вы можете рассматривать это как обычную функцию и вызывать ее, написав (|P|_|) argument или вы можете рассматривать его как значение и передавать его в качестве аргумента другим функциям или параметризованным активным шаблонам. Ваш код будет работать, если вы реализовали Chain как шаблон, принимающий обычные функции:

let (|Chain|_|) f g data =
    f data |> Option.bind (fun r -> g data)

затем Chain <arg1> <arg2> <pat> просто вызывает параметризованный активный шаблон с двумя функциями в качестве аргумента. При вызове связывает результат с шаблоном <pat>, В приведенном выше примере два аргумента являются значениями функций, представляющими шаблоны (это могут быть обычные функции, но не лямбда-функции из-за синтаксических ограничений).

Другие вопросы по тегам