Programing

Haskell : 튜플을 매핑하는 방법?

lottogame 2020. 12. 6. 20:52
반응형

Haskell : 튜플을 매핑하는 방법?


Haskell에서는 목록을 쉽게 매핑 할 수 있습니다.

map (\x -> 2*x) [1,2]

나에게 준다 [2,4]. 그렇게 작동하는 "mapTuple"함수가 있습니까?

mapTuple (\x -> 2*x) (1,2)

결과는 (2,4).


Hoogle에서 검색(a -> b) -> (a, a) -> (b, b) 하면 필요한 유형 인에 대해 정확히 일치 하지 않지만 직접 수행하는 것은 매우 쉽습니다.

mapTuple :: (a -> b) -> (a, a) -> (b, b)
mapTuple f (a1, a2) = (f a1, f a2)

참고로, 3- 튜플, 4- 튜플 등에 대한 새 함수를 정의해야합니다. 이러한 필요성은 신호일 수 있지만 의도 한대로 튜플을 사용하지 않는다는 의미입니다. 일반적으로 튜플은 다른 유형의 값을 보유합니다. 따라서 모든 값에 단일 함수를 적용하려는 것은 그리 일반적이지 않습니다.


다음은 다소 짧은 점없는 솔루션입니다.

import Control.Monad (join)
import Control.Arrow ((***))

mapTuple = join (***)

다음을 사용할 수 있습니다 Bifunctor.

import Control.Monad  (join)
import Data.Bifunctor (bimap)

join bimap (2*) (1,2)

이것은 쌍뿐만 아니라 여러 다른 유형 (예 : Either.

Bifunctor버전 4.8 부터 기본 입니다. 이전에는 bifunctors 패키지 에서 제공했습니다 .


모듈의 화살표사용 Control.Arrow하여 튜플에서 작동하는 함수를 작성할 수 있습니다 .

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)

그러면 mapTuple이

mapTuple f = f *** f

질문에 대해 임의의 배열의 튜플을 매핑하는 함수를 요청했다면 다른 유형 (예 : 튜플 유형 (a,b)(a,b,c)완전히 다르고 관련이 없음)을 가지기 때문에 그렇게 할 수 없습니다 .


렌즈사용 하여 튜플을 매핑 할 수도 있습니다 .

import Control.Lens
mapPair = over both

또는 최대 10 개의 요소로 튜플을 매핑 할 수 있습니다.

mapNtuple f = traverseOf each (return . f)

다른 방법이 있습니다.

mapPair :: (a -> b) -> (a, a) -> (b, b) -- this is the inferred type
mapPair f = uncurry ((,) `on` f)

당신은 필요 Data.Function수입 on기능.


이 다채로운 세트에 다른 솔루션을 추가하려면 ... Scrap-Your-Boilerplate 일반 프로그래밍을 사용하여 임의의 n- 튜플에 매핑 할 수도 있습니다 . 예를 들면 :

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)

SYB는 유형별로 필드를 선택하므로 명시 적 유형 주석이 중요합니다. Float예를 들어 하나의 튜플 요소 유형을 만들면 더 이상 두 배가되지 않습니다.


예,이 개 항목의 튜플을 위해, 당신은 사용할 수 있습니다 firstsecond튜플의 내용을 매핑 (음주 유형 서명에 대해 걱정하지를; a b c로 읽을 수있는 b -> c이 상황에서). 더 큰 튜플의 경우 데이터 구조와 렌즈를 대신 사용하는 것이 좋습니다.


추가 패키지는 제공 both의 기능 Data.Tuple.Extra의 모듈을. 문서에서 :

Apply a single function to both components of a pair.

> both succ (1,2) == (2,3)

both :: (a -> b) -> (a, a) -> (b, b)

You can also use Applicatives which have additional benefit of giving you possibility to apply different functions for each tuple element:

import Control.Applicative

mapTuple :: (a -> a') -> (b -> b') -> (a, b) -> (a', b')
mapTuple f g = (,) <$>  f . fst <*> g . snd

Inline version:

(\f -> (,) <$>  f . fst <*> f . snd) (*2) (3, 4)

or with different map functions and without lambda:

(,) <$> (*2) . fst <*> (*7) . snd $ (3, 4)

Other possibility would be to use Arrows:

import Control.Arrow

(+2) . fst &&& (+2) . snd $ (2, 3)

The uniplate package provides the descend function in the Data.Generics.Uniplate.Data module. This function will apply the function everywhere the types match, so can be applied to lists, tuples, Either, or most other data types. Some examples:

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]

I just added a package tuples-homogenous-h98 to Hackage that solves this problem. It adds newtype wrappers for tuples and defines Functor, Applicative, Foldable and Traversable instances for them. Using the package you can do things like:

untuple2 . fmap (2 *) . Tuple2 $ (1, 2)

or zip tuples like:

Tuple2 ((+ 1), (*2)) <*> Tuple2 (1, 10)

Yes, you would do:

map (\x -> (fst x *2, snd x *2)) [(1,2)]

fst grabs the first data entry in a tuple, and snd grabs the second; so, the line of code says "take a tuple, and return another tuple with the first and second items double the previous."

참고URL : https://stackoverflow.com/questions/9722689/haskell-how-to-map-a-tuple

반응형