2012-03-15 15 views
48

हास्केल में, मैं आसानी से एक सूची मैप कर सकते हैं:हास्केल: टुपल को कैसे मैप करें?

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

मुझे [2,4] देता है। क्या कोई "मैपटुप" फ़ंक्शन है जो इस तरह काम करेगा?

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

परिणामस्वरूप (2,4) है।

+0

बीटीडब्ल्यू, सूची में एक साधारण गुणा को मैप करने के लिए लैम्ब्स का उपयोग करने की आवश्यकता नहीं है: बस 'नक्शा (* 2) [1,2,3] 'चाल चल जाएगा। –

+1

ध्यान दें कि इससे कोई फर्क नहीं पड़ता कि आप इसे कैसे कार्यान्वित करते हैं, यह केवल प्रकार के साथ टुपल्स पर काम करेगा: (ए, ए) – hololeap

उत्तर

41

Searching at Hoogle(a -> b) -> (a, a) -> (b, b), जो प्रकार आप की आवश्यकता है के लिए कोई सटीक मिलान देता है, लेकिन यह काफी अपने आप को ऐसा करने के लिए आसान है:

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

ध्यान दें, आप 3-tuples के लिए एक नया समारोह को परिभाषित करना होगा, 4-ट्यूपल्स इत्यादि - यद्यपि ऐसी आवश्यकता एक संकेत हो सकती है, कि आप टुपल्स का उपयोग नहीं कर रहे हैं जैसे कि उनका इरादा था: आम तौर पर, टुपल्स को विभिन्न प्रकार के मान होते हैं, इसलिए सभी मानों पर एक ही फ़ंक्शन लागू करना चाहते हैं, यह बहुत आम नहीं है।

+0

धन्यवाद, यही वह जवाब है जिसे मैं ढूंढ रहा था। –

+7

'मानक। एरो' * मानक libs में * है, और 'Control.Bifunctor' बहुत दूर नहीं है ... – Landei

+0

@Landei: हाँ, लेकिन ओपी ने एक टुपल पर एक ही फ़ंक्शन मैप करने के बारे में पूछा। – Boris

3

हाँ, आप क्या करेंगे:

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

fst एक टपल में पहली डेटा प्रविष्टि पकड़ लेता है, और snd दूसरा पकड़ लेता है; इसलिए, कोड की रेखा कहती है "एक ट्यूपल लें, और पहले और दूसरी वस्तुओं के साथ एक और टुपल वापस करें।"

+1

आपका कोड काम नहीं करता है, लेकिन एक साइड नोट के रूप में याद रखें कि आप लैम्बडा फ़ंक्शन के तर्कों पर पैटर्न मिलान कर सकते हैं : '\ (x, y) -> (x * 2, y * 2)' – danr

+0

@danr - मुझे एक कोष्ठक याद आ रहा था, इसे इंगित करने के लिए धन्यवाद: पी। मुझे पैटर्न मिलान के बारे में पता नहीं था, धन्यवाद! –

+0

@danr यह मेरे लिए काम करता है। –

9

हाँ, 2 आइटम की tuples के लिए, आप first और second उपयोग कर सकते हैं एक टपल की सामग्री को मैप करने के लिए (प्रकार हस्ताक्षर के बारे में चिंता मत करो; a b c इस स्थिति में b -> c के रूप में पढ़ा जा सकता है)। बड़े tuples के लिए, आप इसके बजाय एक डेटा संरचना और लेंस का उपयोग करने पर विचार करना चाहिए।

22

आप मॉड्यूल Control.Arrow से arrows का उपयोग कर सकते हैं जो टुपल्स पर काम करने वाले कार्यों को लिखने के लिए करते हैं।

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 

अगर आपका प्रश्न आप एक समारोह है कि मनमाने ढंग से arity की tuples की मानचित्रों के लिए कहा साथ, तो मुझे डर लग रहा तुम नहीं है क्योंकि वे विभिन्न प्रकार के लिए होता कर सकते हैं (उदाहरण के लिए ट्यूपल प्रकार (a,b) और (a,b,c) पूरी तरह से अलग और असंबंधित हैं)।

+0

क्या मुझे उनका उपयोग करने के लिए मॉड्यूल लोड करने की आवश्यकता है? सादा प्रस्ताव में आपका कोड काम नहीं करता है। –

+0

हाँ, आप करते हैं, आपको 'Control.Arrow' की आवश्यकता है। मैंने अपना जवाब अपडेट किया। –

22

आप Bifunctor इस्तेमाल कर सकते हैं:

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

join bimap (2*) (1,2) 

यह रूप में अच्छी तरह से काम करता है न केवल जोड़े के लिए है, लेकिन अन्य प्रकार के एक नंबर के लिए, उदाहरण के लिए Either के लिए।

Bifunctor संस्करण 4.8 के रूप में base में है। पहले इसे bifunctors पैकेज द्वारा प्रदान किया गया था।

+0

'(->)' monad उदाहरण के लिए 'join' चाल अद्भुत है। धन्यवाद! – Profpatsch

+0

अच्छा! जो मुझे किसी अन्य संदर्भ में मिला, वह धड़कता है, जहां मैं केवल दूसरे मान को संशोधित करना चाहता था, यानी आयात 'डेटा.ग्राफ.इंडक्टिव.Query.मोनाड' और फिर '(* 2)><(* 2) 'यह फ़ंक्शन है बाद में (मुझे परेशान है कि यह डेटा में नहीं है। ठीक है, लेकिन शायद यह इतिहास का दुर्घटना है।) –

71

यहाँ एक नहीं बल्कि छोटे बिंदु से मुक्त समाधान है:

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

mapTuple = join (***) 
+0

फ़ंक्शंस पर उपयोग किए जाने पर शामिल होने का क्या प्रभाव है? –

+12

@ रिकार्डो - 'जॉइन' एक ही प्रकार, 'ए-> ए-> बी' के साथ दो तर्कों का एक फ़ंक्शन लेता है, और एक तर्क' ए -> बी' के साथ एक नया फ़ंक्शन बनाता है, जो दोनों पदों के लिए उस तर्क को पास करता है मूल कार्य ऐसा इसलिए है क्योंकि फ़ंक्शंस के लिए मोनाड इंस्टेंस 'रीडर' मोनैड के समान है, 'join' type' (a -> a -> b) -> a -> b' दे रहा है। जब तीर इंफिक्स नहीं लिखा जाता है, तो मुझे यह काम करना थोड़ा आसान लगता है, उदा। 'शामिल हों :: (ए ->) ((ए ->) बी) -> (ए ->) बी'। –

+0

@ जॉन: धन्यवाद! –

12

इस रंगीन सेट करने के लिए एक और समाधान जोड़ने के लिए ... तुम भी अधिक मनमाना एन-tuples Scrap-Your-Boilerplate generic programming का उपयोग कर देख सकते हैं।उदाहरण के लिए:

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 प्रकार के अनुसार फ़ील्ड का चयन करता है। यदि कोई एक tuple तत्व प्रकार Float बनाता है, उदाहरण के लिए, अब यह दोगुना नहीं होगा।

+1

मैं अस्पष्टता से समझता हूं कि मूल/एसबीएलर में कुछ समर्थन के साथ जीएचसी जेनरिक द्वारा कई मूल एसवाईबी पुस्तकालयों को प्रतिस्थापित किया गया है। (लेकिन हो सकता है कि आप और वह विकिपीडिया पृष्ठ सामान्य रूप से जेनेरिक के लिए एक शब्द "एसवाईबी" का उपयोग करें, न केवल विशिष्ट मूल एसवाईबी कागजात ...) – misterbee

+3

मैं गलत हो सकता हूं, लेकिन जीएचसी के ['डेटा। डेटा 'दस्तावेज] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Data.html) SYB को उद्धृत करता है, मैं डरता हूं कि यह प्रत्यक्ष वंशज है। –

6

तुम भी Applicatives जो प्रत्येक टपल तत्व के लिए विभिन्न कार्यों को लागू करने के आप संभावना देने के अतिरिक्त लाभ भी उठा उपयोग कर सकते हैं:

import Control.Applicative 

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

इनलाइन संस्करण:

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

या अलग नक्शे कार्यों के साथ और बिना लैम्ब्डा:

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

,210

अन्य संभावना तीर का उपयोग करने के होगा:

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

आप Data.Functionon समारोह के लिए आयातित की जरूरत है:

import Control.Arrow 

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

यहाँ एक और तरीका है।

+0

मुझे लगता है कि यह सबसे सरल और सबसे सुरुचिपूर्ण समाधान है। प्रत्येक 0 से अधिक – hololeap

4

मैंने अभी इस पैकेज को हल करने वाले हैकेज में पैकेज tuples-homogenous-h98 जोड़ा है। यह newtype रैपर को टुपल्स के लिए जोड़ता है और Functor, Applicative, Foldable और Traversable उनके लिए उदाहरण परिभाषित करता है। पैकेज का उपयोग करके आप की तरह कर सकते हैं:

untuple2 . fmap (2 *) . Tuple2 $ (1, 2) 
तरह

या ज़िप tuples:

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

तुम भी lens उपयोग कर सकते हैं tuples मैप करने के लिए:

import Control.Lens 
mapPair = over both 

या आप tuples की मैप कर सकते हैं 10 तत्वों के साथ:

mapNtuple f = traverseOf each (return . f) 
+2

'काम करने के लिए भी लगता है: '(प्रत्येक पर) (+1) (1,2,3,4,5,6,7,8,9) == (2,3,4,5 , 6,7,8,9,10) ' – Wizek

5

extra पैकेज फ़ंक्शन 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) 
4

uniplate पैकेज Data.Generics.Uniplate.Data मॉड्यूल में descend समारोह प्रदान करता है। यह फ़ंक्शन हर प्रकार के मिलान के फ़ंक्शन को लागू करेगा, इसलिए सूचियों, tuples, या तो, या अन्य सभी डेटा प्रकारों पर लागू किया जा सकता है। कुछ उदाहरण:

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]