Haskell Language
Składnia w funkcjach
Szukaj…
Gwardia
Funkcję można zdefiniować za pomocą strażników, które można pomyśleć o klasyfikowaniu zachowań według danych wejściowych.
Weź następującą definicję funkcji:
absolute :: Int -> Int -- definition restricted to Ints for simplicity
absolute n = if (n < 0) then (-n) else n
Możemy to zmienić za pomocą osłon:
absolute :: Int -> Int
absolute n
| n < 0 = -n
| otherwise = n
W tym kontekście otherwise
jest znaczącym aliasem dla True
, więc zawsze powinien być ostatnim strażnikiem.
Dopasowywanie wzorów
Haskell obsługuje wyrażenia pasujące do wzorca zarówno w definicji funkcji, jak i poprzez instrukcje case
.
Instrukcja case jest bardzo podobna do przełącznika w innych językach, tyle że obsługuje wszystkie typy Haskell.
Zacznijmy od prostych:
longName :: String -> String
longName name = case name of
"Alex" -> "Alexander"
"Jenny" -> "Jennifer"
_ -> "Unknown" -- the "default" case, if you like
Lub możemy zdefiniować naszą funkcję jak równanie, które byłoby dopasowaniem wzorca, bez użycia instrukcji case
:
longName "Alex" = "Alexander"
longName "Jenny" = "Jennifer"
longName _ = "Unknown"
Bardziej powszechnym przykładem jest typ Maybe
:
data Person = Person { name :: String, petName :: (Maybe String) }
hasPet :: Person -> Bool
hasPet (Person _ Nothing) = False
hasPet _ = True -- Maybe can only take `Just a` or `Nothing`, so this wildcard suffices
Dopasowywania wzorców można również używać na listach:
isEmptyList :: [a] -> Bool
isEmptyList [] = True
isEmptyList _ = False
addFirstTwoItems :: [Int] -> [Int]
addFirstTwoItems [] = []
addFirstTwoItems (x:[]) = [x]
addFirstTwoItems (x:y:ys) = (x + y) : ys
W rzeczywistości dopasowanie wzorca może być stosowane na dowolnym konstruktorze dla dowolnej klasy typu. Np. Konstruktorem list jest :
a dla krotek ,
Używanie gdzie i strażników
Biorąc pod uwagę tę funkcję:
annualSalaryCalc :: (RealFloat a) => a -> a -> String
annualSalaryCalc hourlyRate weekHoursOfWork
| hourlyRate * (weekHoursOfWork * 52) <= 40000 = "Poor child, try to get another job"
| hourlyRate * (weekHoursOfWork * 52) <= 120000 = "Money, Money, Money!"
| hourlyRate * (weekHoursOfWork * 52) <= 200000 = "Ri¢hie Ri¢h"
| otherwise = "Hello Elon Musk!"
Możemy użyć where
aby uniknąć powtórzeń i uczynić nasz kod bardziej czytelnym. Zobacz alternatywną funkcję poniżej, używając where
:
annualSalaryCalc' :: (RealFloat a) => a -> a -> String
annualSalaryCalc' hourlyRate weekHoursOfWork
| annualSalary <= smallSalary = "Poor child, try to get another job"
| annualSalary <= mediumSalary = "Money, Money, Money!"
| annualSalary <= highSalary = "Ri¢hie Ri¢h"
| otherwise = "Hello Elon Musk!"
where
annualSalary = hourlyRate * (weekHoursOfWork * 52)
(smallSalary, mediumSalary, highSalary) = (40000, 120000, 200000)
Jak zaobserwowano, że stosuje się where
w zakończeniu korpusu funkcji eliminuje powtarzanie obliczeń ( hourlyRate * (weekHoursOfWork * 52)
), a także stosowane where
zorganizować zakres wynagrodzenia.
Nazwy typowych podwyrażeń można również uzyskać za pomocą wyrażeń let
, ale tylko tam, where
składnia umożliwia strażnikom odwoływanie się do nazwanych podwyrażeń.