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