Przeglądając Haskell Prelude, widzę funkcję const :
const x _ = x
Nie mogę znaleźć nic odpowiedniego do tej funkcji.
Jaki jest sens? Czy ktoś może podać przykład, gdzie można zastosować tę funkcję?
Przeglądając Haskell Prelude, widzę funkcję const :
const x _ = x
Nie mogę znaleźć nic odpowiedniego do tej funkcji.
Jaki jest sens? Czy ktoś może podać przykład, gdzie można zastosować tę funkcję?
Odpowiedzi:
Przydaje się do przechodzenia do funkcji wyższego rzędu, gdy nie potrzebujesz całej ich elastyczności. Na przykład monadyczny operator sekwencji >>można zdefiniować w kategoriach monadycznego operatora wiązania jako
x >> y = x >>= const y
Jest to trochę schludniejsze niż użycie lambdy
x >> y = x >>= \_ -> y
i możesz go nawet używać bez punktów
(>>) = (. const) . (>>=)
chociaż nie polecam tego szczególnie w tym przypadku.
map (const 42) [1..5]skutkuje [42, 42, 42, 42, 42].
constjest przydatny do stosowania do pojedynczego argumentu w celu uzyskania funkcji, gdy jest ona potrzebna (na przykład przekazywanie do map).
head = foldr const (error "Prelude.head: empty list")
Aby dodać do doskonałej bezpośredniej odpowiedzi Hammara: skromne funkcje, takie jak consti idsą naprawdę przydatne jako funkcje wyższego rzędu z tego samego powodu, z którego są fundamentalne w rachunku kombinatorów SKI .
Nie sądzę, żebym uważał, że funkcje preludium haskella były świadomie wzorowane na tym formalnym systemie czy czymkolwiek. Tyle, że tworzenie bogatych abstrakcji w haskell jest bardzo łatwe, więc często widzisz, że tego typu teoretyczne rzeczy okazują się praktycznie przydatne.
Wtyczka bezczelny, ale na blogu o tym, jak na przykład aplikacyjnych (->)są rzeczywiście Si Kkombinatorów tutaj , jeśli to rodzaj rzeczy, jesteś w.
((->) e)jest również monadą czytelnika - z Readeri tym podobnymi po prostu newtypeopakowaniami - a askfunkcja jest wtedy id, więc jest to również Ikombinator. Jeśli spojrzeć zamiast na podstawie oryginalnego bckw Haskell Curry, w B, Ki Wto fmap, returni joinodpowiednio.
Prostym przykładem użycia constjest Data.Functor.(<$). Dzięki tej funkcji można powiedzieć: mam tu funktor z czymś nudnym, ale zamiast tego chcę mieć w sobie tę inną interesującą rzecz, bez zmiany kształtu funktora. Na przykład
import Data.Functor
42 <$ Just "boring"
--> Just 42
42 <$ Nothing
--> Nothing
"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]
Definicja jest następująca:
(<$) :: a -> f b -> f a
(<$) = fmap . const
lub napisane nie tak bezcelowe:
cool <$ uncool = fmap (const cool) uncool
Widzisz, jak constsłuży tutaj do „zapomnienia” o wejściu.
Nie mogę znaleźć nic odpowiedniego do tej funkcji.
Wiele innych odpowiedzi dotyczy stosunkowo ezoterycznych (przynajmniej dla nowicjuszy) zastosowań const. Oto prosty: możesz go użyć, constaby pozbyć się lambdy, która przyjmuje dwa argumenty, odrzuca pierwszy, ale robi coś interesującego z drugim.
Na przykład, następujący (nieefektywne ale pouczające) realizacja length,
length' = foldr (\_ acc -> 1 + acc) 0
można przepisać jako
length' = foldr (const (1+)) 0
co jest być może bardziej eleganckie.
Wyrażenie const (1+)jest rzeczywiście semantycznie równoważne z \_ acc -> 1 + acc, ponieważ pobiera jeden argument, odrzuca go i zwraca sekcję (1+) .
Innym zastosowaniem jest implementacja funkcji składowych klasy, które mają fikcyjny argument, który nie powinien być oceniany (używany do rozwiązywania niejednoznacznych typów). Przykład, który może znajdować się w Data.bits:
instance Bits Int where
isSigned = const True
bitSize = const wordSize
...
Używając const, wyraźnie mówimy, że definiujemy wartości stałe.
Osobiście nie podoba mi się używanie fikcyjnych parametrów, ale jeśli są one używane w klasie, jest to raczej przyjemny sposób pisania instancji.
constmoże być po prostu implementacją, której szukasz w połączeniu z innymi funkcjami. Oto przykład, który odkryłem.
Powiedzmy, że chcemy przepisać strukturę 2-krotek do innej struktury 2-krotek. Mógłbym to wyrazić następująco:
((a,b),(c,d)) ⇒ (a,(c,(5,a)))
Mogę podać prostą definicję z dopasowywaniem wzorców:
f ((a,b),(c,d)) = (a,(c,(5,a)))
A jeśli chcę bezcelowego (milczącego) rozwiązania tego rodzaju przepisywania? Po pewnym czasie myślenia i zabawy, odpowiedź jest taka, że możemy wyrazić dowolne przepisanie za pomocą (&&&), const, (.), fst, snd. Zauważ, że (&&&)pochodzi z Control.Arrow.
Rozwiązanie przykładu wykorzystującego te funkcje to:
(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))
Zwróć uwagę na podobieństwo z (a,(c,(5,a))). Co jeśli zastąpimy &&&z ,? Następnie brzmi:
(fst.fst, (fst.snd, (const 5, fst.fst)))
Zwróć uwagę, jaki ajest pierwszy element pierwszego elementu i to jest to fst.fst, co projektuje. Zwróć uwagę, jaki cjest pierwszy element drugiego elementu i to jest to fst.snd, co projektuje. Oznacza to, że zmienne stają się ścieżką do ich źródła.
constpozwala nam na wprowadzenie stałych. Ciekawe, jak nazwa pasuje do znaczenia!
I wtedy uogólnić ten pomysł z aplikacyjnych, dzięki czemu można napisać dowolną funkcję w bezcelowym stylu (tak długo, jak masz analiza case dostępne funkcje, takie jak maybe, either, bool). Ponownie constodgrywa rolę wprowadzania stałych. Możesz zobaczyć tę pracę w pakiecie Data.Function.Tacit .
Kiedy zaczynasz abstrakcyjnie, od celu, a następnie pracujesz nad wdrożeniem, możesz być zaskoczony odpowiedziami. Oznacza to, że każda funkcja może być tak tajemnicza, jak każdy trybik w maszynie. Jeśli jednak cofniesz się, aby zobaczyć całą maszynę, możesz zrozumieć kontekst, w którym ten trybik jest potrzebny.
Powiedzmy, że chcesz utworzyć listę Nothingsrówną długości ciągu. As constzwraca swój pierwszy argument, bez względu na drugi, możesz zrobić:
listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing
lub bardziej szczegółowo:
listOfNothing st = map (const Nothing) st
Powiedz, że chcesz obrócić listę. To idiomatyczny sposób na zrobienie tego w Haskell:
rotate :: Int -> [a] -> [a]
rotate _ [] = []
rotate n xs = zipWith const (drop n (cycle xs)) xs
Ta funkcja zamyka dwie tablice za pomocą funkcji const, pierwsza to nieskończona tablica cykliczna, a druga to tablica, od której zacząłeś.
const działa jak kontrola granic i używa oryginalnej tablicy do zakończenia tablicy cyklicznej.
Zobacz: Obracanie listy w Haskell
Nie mogę znaleźć nic odpowiedniego do tej funkcji.
Załóżmy, że chcesz wygenerować wszystkie podciągi z danej listy.
Dla każdego elementu listy w danym momencie masz do wyboru True (uwzględnij go w bieżącym podciągu) lub False (nie uwzględniaj go). Można to zrobić za pomocą funkcji filterM .
Lubię to:
λ> import Control.Monad
λ> :t filterM
filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
λ>
Na przykład chcemy, aby wszystkie podciągi [1..4].
λ> filterM (const [True, False]) [1..4]
[[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
λ>
backgroundColor :: Text -> Colorjest dla mniebackgroundColor = const White