2013-03-14 20 views
7

Powiedzmy, że mam funkcję, która wykonuje pewne obliczenia, z kilkoma wzorami; realizowane w postaci dopasowywania wzorców.Czy Haskell dopuszcza wyrażenie let dla wielu wzorców dopasowania?

Większość z tych wzorów (wraz z innymi rzeczami, które różnią się od siebie) traktuje parametr, dla którego używam zmiennej pośredniej w wyrażeniu let. Ale uważam, że jest naprawdę zbędne mieć ten sam let na wielu wzorcach, i zastanawiam się, czy istnieje sposób zdefiniowania let dla kilku wzorców?

Oto przykład z moim powielane let:

data MyType a = Something a | Another Int [a] 

myFunc (Something x) = -- return something, this isn't the point here 
myFunc (Another 0 xs) = 
    let intermediary = some $ treatment xs 
    in doSthg intermediary 1 
myFunc (Another 1 (x:xs)) = 
    let intermediary = some $ treatment xs 
    in doSthg1 intermediary 1 x 
myFunc (Another 2 (x:x':xs)) = 
    let intermediary = some $ treatment xs 
    in doSthg2 intermediary 2 x x' 

Można zobaczyć, że parametr xs występuje zawsze, gdy używam go do intermediary, a to może być factorised. Można to łatwo osiągnąć za pomocą funkcji pomocnika, ale zastanawiałem się, czy to, o co pytam, jest możliwe bez niego. Staraj się zachować prostotę dla początkujących i mam nadzieję, że mój przykład jest wystarczająco jasny.

+2

Nie! Musimy użyć oddzielnej funkcji i przekazać ją jako parametr jawnie, aby wyodrębnić powtarzający się kod. Jeśli chodzi o Haskella, powyższe wystąpienia 'xs' są całkowicie różnymi zmiennymi (ponieważ mają różne miejsca wiązania). – luqui

+0

Dzięki! Teraz mam inne pytanie: czy planowałeś, że równania trzecie i czwarte nigdy się nie zgadzają? Ponieważ 'xs' pasuje do dowolnej listy ... Czy odwróciłeś kolejność równań? – yatima2975

+0

Napisałem to, aby zilustrować, że 'xs' jest wyodrębniane z dopasowywania wzorca w kilku wzorach, ale nie zawsze ma to samo znaczenie (w przeciwnym razie dopasowanie wzorca nie byłoby interesujące: byłby tylko jeden wzorzec). To, co w tym przypadku zostało zrobione, nie jest ważne; ale w moim kodzie "y" jest zastępowane rzeczywistymi wartościami, więc późniejsze wzory pasują do siebie. Ale masz rację, więc zmienię go na konkretny typ, aby wszystko było bardziej zrozumiałe. –

Odpowiedz

7

Ten szczególności problem można obejść w następujący sposób:

myFunc2 (Something x) = returnSomething x 
myFunc2 (Another n ys) = 
    let xs = drop n ys 
     x = head ys 
     x' = head (tail ys) 
     intermediate = some $ treatment xs 
    in case n of 
     0 -> doSomething intermediate n 
     1 -> doSomething1 intermediate n x 
     2 -> doSomething2 intermediate n x x' 

Dzięki leniwe oceny x i x' będą oceniane tylko wtedy, gdy potrzebna jest ich wartość.

Jednak - i to jest jednak duże! - Twój kod da błąd runtime, gdy próbujesz zadzwonić pod numer myFunc2 (Another 2 []) (i jeśli doSomething2 faktycznie używa x!), ponieważ aby dowiedzieć się, co to jest x, musimy ocenić head ys - a to spowoduje awarię dla pustej listy. Kod podany jako przykład również nie zadziała (inny błąd środowiska wykonawczego) dla Another 2 [], ponieważ nie ma zgodnego wzorca, ale tam łatwiej jest podać przypadek cofnięcia.

To może nie być problem, jeśli kontrolujesz wejście i zawsze upewnij się, że lista w Another jest wystarczająco długa, ale ważne jest, aby być świadomym tego problemu!

+0

To nie jest problem w moim kodzie, ponieważ 'Another' obiekty nie mogą być budowane z pustymi listami. +1, robi to bardzo dobrze –

+0

@tehinternetsismadeofcatz: cały problem ma silny "interpretator języka stosu" - poczuj go, więc jeśli skompilujesz poprawnie, zaczniesz działać poprawnie - system typu po prostu nie jest (ale jest blisko!) wystarczająco silny, aby wyrazić to, co wiesz * o danych wejściowych. Możesz także rozważyć zmianę 'MyType', aby wyraźniej wyrazić to, co wiesz o argumentach, ale to dużo pracy ... – yatima2975