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ę?
backgroundColor :: Text -> Color
jest dla mniebackgroundColor = const White
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 jakox >> 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
chociaż nie polecam tego szczególnie w tym przypadku.
źródło
map (const 42) [1..5]
skutkuje[42, 42, 42, 42, 42]
.const
jest przydatny do stosowania do pojedynczego argumentu w celu uzyskania funkcji, gdy jest ona potrzebna (na przykład przekazywanie domap
).head = foldr const (error "Prelude.head: empty list")
Aby dodać do doskonałej bezpośredniej odpowiedzi Hammara: skromne funkcje, takie jak
const
iid
są 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ścieS
iK
kombinatorów tutaj , jeśli to rodzaj rzeczy, jesteś w.źródło
((->) e)
jest również monadą czytelnika - zReader
i tym podobnymi po prostunewtype
opakowaniami - aask
funkcja jest wtedyid
, więc jest to równieżI
kombinator. Jeśli spojrzeć zamiast na podstawie oryginalnego bckw Haskell Curry, wB
,K
iW
tofmap
,return
ijoin
odpowiednio.Prostym przykładem użycia
const
jestData.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ładimport Data.Functor 42 <$ Just "boring" --> Just 42 42 <$ Nothing --> Nothing "cool" <$ ["nonsense","stupid","uninteresting"] --> ["cool","cool","cool"]
Definicja jest następująca:
lub napisane nie tak bezcelowe:
cool <$ uncool = fmap (const cool) uncool
Widzisz, jak
const
służy tutaj do „zapomnienia” o wejściu.źródło
Wiele innych odpowiedzi dotyczy stosunkowo ezoterycznych (przynajmniej dla nowicjuszy) zastosowań
const
. Oto prosty: możesz go użyć,const
aby 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+)
.źródło
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.
źródło
const
moż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 zControl.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
a
jest pierwszy element pierwszego elementu i to jest tofst.fst
, co projektuje. Zwróć uwagę, jakic
jest pierwszy element drugiego elementu i to jest tofst.snd
, co projektuje. Oznacza to, że zmienne stają się ścieżką do ich źródła.const
pozwala 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
). Ponownieconst
odgrywa 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.
źródło
Powiedzmy, że chcesz utworzyć listę
Nothings
równą długości ciągu. Asconst
zwraca 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
źródło
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
źródło
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],[]] λ>
źródło