2009-08-28 13 views
12

Poniższy kod C# działa jak się spodziewałem, drukowanie `łączone jako„A”:Dlaczego nie można sparametryzować nie-częściowych aktywnych wzorców w F #?

let (|Char|_|) convf = function 
    | LazyList.Nil -> None 
    | LazyList.Cons (x, _) -> Some (convf x) 

let test = function 
    | Char System.Char.ToUpper x -> printfn "Matched as %A" x 
    | _ -> printfn "Didn't match" 

test (LazyList.of_list ['a']) 

Jednak jeśli zmienię Char z częściowego aktywnego wzoru do całkowitego aktywnego wzoru następująco:

let (|Char|NoChar|) convf = function 
    | LazyList.Nil -> NoChar 
    | LazyList.Cons (x, _) -> Char x 

let test = function 
    | Char System.Char.ToUpper x -> printfn "Matched as %A" x 
    | NoChar System.Char.ToUpper -> printfn "Didn't match" 

test (LazyList.of_list ['a']) 

Następnie kod nie skompilować, dając się następujący komunikat o błędzie: error FS0191: Only active patterns returning exactly one result may accept arguments.

Ten przykład może wyglądać nieco wymyślony, ale to uproszczona wersja aktywnego wzoru próbowałem użyć w Prolo g lexer, nad którym pracowałem w wolnym czasie. Mogę z łatwością przepisać mój kod, aby uniknąć tego problemu, ale jestem ciekawy, dlaczego ten rodzaj kodu jest niedozwolony.

Aktualizacja: nowsze wersje F # wydaje się przemianowany ten błąd:

error FS0722: Only active patterns returning exactly one result may accept arguments

Odpowiedz

13

NB. Dokładnie tak powiedział Brian, ale mam nadzieję, że wyraził to w jaśniejszy sposób.

Przypominam sobie, że zgłosiłem błąd dotyczący właśnie tego problemu, a IIRC to właśnie Don Syme miał do powiedzenia w tej sprawie.

Wielozadaniowy aktywny wzór to funkcja konwersji z pewnej wartości wejściowej na jedną z kilku wartości wyjściowych. W twoim przykładzie każda postać jest konwertowana do przypadku Char lub NoChar.

Zaletą tego jest fakt, że kompilator F # wywołuje funkcję aktywnego wzoru wielozadaniowego jeden raz, a następnie może ogólnie określić, która reguła dopasowania wzorca ma być następnie oceniona.

Jeśli jednak zezwolisz na parametr, musisz ocenić aktywny wzór wielu liter dla każdej reguły dopasowania wzorca.

Więc wyobrazić następujący

match input with 
| Alpha "foo" -> ... 
| Bravo "bar" -> ... 

Przy ocenie (| Alpha | Bravo |) "foo" powrócił 'Bravo', to pierwsza zasada nie będzie pasować. Likeways (| Alpha | Bravo |) "bar" zwraca "Alpha", wtedy druga reguła również nie pasuje. Tak naprawdę nie masz aktywnego wzorca dla wielu spraw. Tylko paramterujący, częściowo aktywny wzór.(Ponieważ w przypadku niektórych danych wejściowych oczekiwany wzorzec nie zostanie trafiony).

Tak więc w konfrontacji z zakresem języka, który nie ma większego sensu, a w rzeczywistości może być znacznie jaśniejszy dzięki częściowy, sparametryzowany aktywny wzór. Funkcja nie została dodana do języka.

4

nie mogę powiedzieć na pewno, (nie wiem rzeczywiste uzasadnienie projektu), ale stara się odwrócić - Zaprojektuj to, czego można się spodziewać po tym kodzie?

let (|Char|NoChar|) pred = function  
    | LazyList.Nil -> NoChar  
    | LazyList.Cons (x, _) -> if pred x then Char x else NoChar 
let test = function  
    | Char System.Char.IsLetter x -> printfn "Matched as %A" x  
    | NoChar System.Char.IsDigit -> printfn "Didn't match" 
test (LazyList.of_list ['a']) 
test (LazyList.of_list ['1']) 

Zważywszy, że non-częściowe aktywne wzory mają podzielić całą przestrzeń, byłoby dziwne, jeśli dał każdy inny argument, wewnątrz tego samego meczu, bo wtedy mogliby „zarówno upaść” lub „jak odnieść sukces ". (Sugeruje także, w jaki sposób mogą one zostać zaimplementowane, np. Jako wzorce przechwytujące ich argumenty przed wykonaniem meczu. Przechwycony argument będzie niezmieniony we wszystkich gałęziach dopasowania.)

Sugeruje również, że można napisać np.

let test convf l = 
    let (|Char|NoChar|) = function  
     | LazyList.Nil -> NoChar  
     | LazyList.Cons (x, _) -> Char(convf x) 
    match l with 
    | Char x -> printfn "Matched as %A" x  
    | NoChar -> printfn "Didn't match" 
test System.Char.ToUpper (LazyList.of_list ['a']) 

(chociaż nie wiem, czy jest to wygodne/realistyczne dla konkretnej aplikacji).

Powiązane problemy