W Haskell, mogę łatwo mapować listę:Haskell: jak zmapować krotkę?
map (\x -> 2*x) [1,2]
daje mi [2,4]
. Czy istnieje funkcja "mapTuple", która działałaby w ten sposób?
mapTuple (\x -> 2*x) (1,2)
z wynikiem bycia (2,4)
.
W Haskell, mogę łatwo mapować listę:Haskell: jak zmapować krotkę?
map (\x -> 2*x) [1,2]
daje mi [2,4]
. Czy istnieje funkcja "mapTuple", która działałaby w ten sposób?
mapTuple (\x -> 2*x) (1,2)
z wynikiem bycia (2,4)
.
Searching at Hoogle nie podaje dokładnych wyników dla (a -> b) -> (a, a) -> (b, b)
, która jest typem jest potrzebna, ale jest to dość łatwo zrobić samemu:
mapTuple :: (a -> b) -> (a, a) -> (b, b)
mapTuple f (a1, a2) = (f a1, f a2)
Uwaga, trzeba będzie zdefiniować nową funkcję dla 3-krotki, 4-krotki itp - choć taka potrzeba może być oznaką, że nie używasz krotki jakby były przeznaczone: W ogóle, krotki przytrzymaj wartości różnych typów, więc chcąc zastosować pojedynczą funkcję do wszystkich wartości nie jest bardzo częste.
Tak, zrobiłbyś:
map (\x -> (fst x *2, snd x *2)) [(1,2)]
fst
chwyta pierwszy wpis danych w krotce i snd
łapie drugi; tak więc wiersz kodu mówi "weź krotkę i zwróć kolejną krotkę, a pierwsze i drugie elementy podwoją poprzednią."
Twój kod nie działa, ale na marginesie pamiętaj, że możesz dopasować wzorzec przy argumentach do funkcji lambda : '\ (x, y) -> (x * 2, y * 2)' – danr
@danr - Brakowało mi nawiasu, dzięki za wskazanie: P. Nie wiedziałem jednak o dopasowywaniu wzorów, dzięki! –
@danr To działa dla mnie. –
Możesz użyć z modułu Control.Arrow
, aby komponować funkcje działające na krotkach.
Prelude Control.Arrow> let f = (*2) *** (*2)
Prelude Control.Arrow> f (1,2)
(2,4)
Prelude Control.Arrow> let f' = (*2) *** (*3)
Prelude Control.Arrow> f (2,2)
(4,4)
Prelude Control.Arrow> f' (2,2)
(4,6)
Twój mapTuple staje
mapTuple f = f *** f
Jeśli z Twojego pytania zadawane dla funkcji, która odwzorowuje na krotki arbitralnych liczbę operandów, to obawiam się, że nie może, bo musiałby różne rodzaje (np. typy krotek (a,b)
i (a,b,c)
są całkowicie różne i niezwiązane).
Czy muszę załadować moduł, aby z nich skorzystać? W zwykłym Prelude twój kod nie działa. –
Tak, robisz, potrzebujesz 'Control.Arrow'. Zaktualizowałem swoją odpowiedź. –
Można użyć Bifunctor
:
import Control.Monad (join)
import Data.Bifunctor (bimap)
join bimap (2*) (1,2)
To działa nie tylko dla par, ale dla wielu innych rodzajów, jak również, na przykład dla Either
.
Bifunctor
jest w wersji base od wersji 4.8. Poprzednio był dostarczany przez pakiet bifunctors.
Sztuczka 'join' dla instancji' (->) 'monad jest niesamowita. Dzięki! – Profpatsch
Nice! To bije to, co znalazłem w innym kontekście, gdzie chciałem tylko zmodyfikować drugą wartość, tj. Import'Data.Graph.Indukcyjny.Query.Monad', a następnie '(* 2)><(* 2)' to funkcja, którą Później. (Jestem zdziwiony, że to nie jest w Data.Tuple, ale może to tylko przypadek historii.) –
Oto raczej krótki punkt wolne rozwiązanie:
import Control.Monad (join)
import Control.Arrow ((***))
mapTuple = join (***)
Jaki jest efekt sprzężenia, gdy jest używany w funkcjach? –
@Riccardo - 'join' przyjmuje funkcję dwóch argumentów tego samego typu,' a-> a-> b', i tworzy nową funkcję z jednym argumentem 'a -> b', przekazując ten argument do obu pozycji oryginalna funkcja. Dzieje się tak, ponieważ instancja Monad dla funkcji jest identyczna z monadą 'Reader', dając' join' type '(a -> a -> b) -> a -> b'. Uważam, że nieco łatwiej jest dojść do wniosku, gdy strzałki nie są zapisywane jako infiks, np. 'join :: (a ->) ((a ->) b) -> (a ->) b'. –
@JohnL: thanks! –
Aby dodać inne rozwiązanie do tego kolorowego zestawu ... Można również mapować na dowolnych n-krotki używając Scrap-Your-Boilerplate generic programming.Na przykład:
import Data.Data
import Data.Generics.Aliases
double :: Int -> Int
double = (*2)
tuple :: (Int, Int, Int, Int)
tuple = gmapT (mkT double) (1,2,3,4)
nocie, że wyraźne adnotacje typu są ważne, ponieważ SYB wybiera pola według typu. Jeśli na przykład zostanie utworzony jeden element krotki: Float
, nie będzie on już więcej podwójny.
Rozumiem, że wiele oryginalnych bibliotek SYB zostało zastąpionych przez GHC Generics z pewną obsługą w bazie/kompilatorze. (Ale może ty i ta strona wikipedii używają słowa "SYB" jako ogólnego terminu dla generycznych, a nie tylko konkretnych oryginalnych dokumentów SYB ...) – misterbee
Mogę się mylić, ale biorąc pod uwagę, że GHC [dokumentacja "Data.Data"] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Data.html) cytuje SYB, założę się, że jest to bezpośredni potomek. –
Można również użyć Applicatives które mają dodatkową zaletę, że daje możliwość zastosowania różnych funkcji dla każdego elementu krotki: Wersja
import Control.Applicative
mapTuple :: (a -> a') -> (b -> b') -> (a, b) -> (a', b')
mapTuple f g = (,) <$> f . fst <*> g . snd
Inline:
(\f -> (,) <$> f . fst <*> f . snd) (*2) (3, 4)
lub z różnych funkcji map i bez lambda:
(,) <$> (*2) . fst <*> (*7) . snd $ (3, 4)
Inne możliwością byłoby użyć strzałek:
import Control.Arrow
(+2) . fst &&& (+2) . snd $ (2, 3)
Oto kolejny sposób:
mapPair :: (a -> b) -> (a, a) -> (b, b) -- this is the inferred type
mapPair f = uncurry ((,) `on` f)
Trzeba Data.Function
importowane do on
funkcji.
Myślę, że to najprostsze i najbardziej eleganckie rozwiązanie. +1 – hololeap
Właśnie dodany pakiet tuples-homogenous-h98 do Hackage, która rozwiązuje ten problem. Dodaje newtype
owijarki za krotki i definiuje Functor
, Applicative
, Foldable
i Traversable
instancje dla nich. Korzystanie z pakietu można robić takie rzeczy jak:
untuple2 . fmap (2 *) . Tuple2 $ (1, 2)
lub zip krotki jak:
Tuple2 ((+ 1), (*2)) <*> Tuple2 (1, 10)
Można również użyć lens mapować krotki:
import Control.Lens
mapPair = over both
Albo można odwzorować na krotki z maksymalnie 10 elementami:
mapNtuple f = traverseOf each (return . f)
'over each' wydaje się również działać:' (over each) (+1) (1,2,3,4,5,6,7,8,9) == (2,3,4,5 , 6,7,8,9,10) " – Wizek
Pakiet extra zapewnia funkcję both
w module Data.Tuple.Extra. Od docs:
Apply a single function to both components of a pair.
> both succ (1,2) == (2,3)
both :: (a -> b) -> (a, a) -> (b, b)
Pakiet uniplate udostępnia funkcję w module Data.Generics.Uniplate.Datadescend. Ta funkcja zastosuje tę funkcję wszędzie, gdzie typy są zgodne, więc może być zastosowana do list, krotek, albo do większości innych typów danych. Kilka przykładów:
descend (\x -> 2*x) (1,2) == (2,4)
descend (\x -> 2*x) (1,"test",Just 2) == (2,"test",Just 4)
descend (\x -> 2*x) (1,2,3,4,5) == (2,4,6,8,10)
descend (\x -> 2*x) [1,2,3,4,5] == [2,4,6,8,10]
BTW, nie trzeba używać lambdas do mapowania prostego mnożenia na liście: po prostu 'map (* 2) [1,2,3]' zrobiłoby lewę. –
Zauważ, że bez względu na to, jak to zaimplementujesz, będzie działać tylko na krotkach z typem: (a, a) – hololeap