2012-05-28 14 views
33

व्यावहारिक उदाहरणों में मुझे लेंस का उपयोग करने के बारे में कोई स्पष्टीकरण नहीं मिल रहा है। हैकेज पेज से यह छोटा पैराग्राफ मुझे सबसे नज़दीक मिला है:लेंस का उपयोग/उपयोगी क्या है?

यह मॉड्यूल संरचना के तत्वों तक पहुंचने और अपडेट करने का एक शानदार तरीका प्रदान करता है। यह डेटा के समान है। एक्सेसर्स, लेकिन थोड़ी अधिक सामान्य और कम निर्भरताएं हैं। मैं विशेष रूप से यह पसंद करता हूं कि यह राज्य मोनैड में नेस्टेड संरचनाओं को कितनी साफ करता है।

तो, उनके लिए क्या उपयोग किया जाता है? अन्य तरीकों से उनके पास क्या फायदे और नुकसान हैं? उन्हें क्यों चाहिए?

+2

आप एडवर्ड Kmett के [लेंस: एक कार्यात्मक इंपीरेटिव] (http://www.youtube.com/watch?v=efv0SQNde5Q) बात देखने का आनंद ले सकते हैं। यह स्कैला में प्रस्तुत किया गया है, लेकिन हास्केल में लेंस की उपयोगिता का अनुवाद स्पष्ट होना चाहिए। –

उत्तर

45

वे डेटा अपडेट पर एक साफ अमूर्तता प्रदान करते हैं, और वास्तव में कभी भी "आवश्यक" नहीं होते हैं। वे आपको किसी समस्या के बारे में एक अलग तरीके से कारण बताते हैं।

सी जैसी कुछ अनिवार्य/"ऑब्जेक्ट उन्मुख" प्रोग्रामिंग भाषाओं में, आपके पास मूल्यों के कुछ संग्रह (चलो उन्हें "structs" कहते हैं) की परिचित अवधारणा है और संग्रह में प्रत्येक मान को लेबल करने के तरीके (लेबल आमतौर पर होते हैं) "फ़ील्ड" कहा जाता है)।

typedef struct { /* defining a new struct type */ 
    float x; /* field */ 
    float y; /* field */ 
} Vec2; 

typedef struct { 
    Vec2 col1; /* nested structs */ 
    Vec2 col2; 
} Mat2; 

फिर आप तो जैसे इस नव परिभाषित प्रकार के मूल्यों को बना सकते हैं:: यह इस तरह की एक परिभाषा की ओर जाता है

data Vec2 = 
    Vec2 
    { vecX :: Float 
    , vecY :: Float 
    } 

data Mat2 = 
    Mat2 
    { matCol1 :: Vec2 
    , matCol2 :: Vec2 
    } 
:

Vec2 vec = { 2.0f, 3.0f }; 
/* Reading the components of vec */ 
float foo = vec.x; 
/* Writing to the components of vec */ 
vec.y = foo; 

Mat2 mat = { vec, vec }; 
/* Changing a nested field in the matrix */ 
mat.col2.x = 4.0f; 

हास्केल में इसी तरह, हम डेटा प्रकार है

इस डेटा प्रकार का उपयोग इस प्रकार किया जाता है:

let vec = Vec2 2 3 
    -- Reading the components of vec 
    foo = vecX vec 
    -- Creating a new vector with some component changed. 
    vec2 = vec { vecY = foo } 

    mat = Mat2 vec2 vec2 

हालांकि, हास्केल में, डेटा संरचना में नेस्टेड फ़ील्ड को बदलने का कोई आसान तरीका नहीं है। ऐसा इसलिए है क्योंकि आपको उस मूल्य के चारों ओर सभी रैपिंग ऑब्जेक्ट्स को फिर से बनाने की आवश्यकता है, जिन्हें आप बदल रहे हैं, क्योंकि हास्केल मान अपरिवर्तनीय हैं। आप हास्केल में ऊपर की तरह एक मैट्रिक्स है, और मैट्रिक्स में ऊपरी दाएँ सेल बदलना चाहते हैं, तो आप इस लिखने के लिए है:

mat2 = mat { matCol2 = (matCol2 mat) { vecX = 4 } } 

यह काम करता है, लेकिन यह अनाड़ी लग रहा है। तो, कोई भी किसके साथ आया, मूल रूप से यह है: यदि आप दो चीजों को एक साथ समूहित करते हैं: एक मूल्य के "गेटटर" (जैसे vecX और matCol2 ऊपर) इसी कार्य के साथ, गेटटर से संबंधित डेटा संरचना को देखते हुए, क्या कर सकते हैं उस मान के साथ एक नई डेटा संरचना बनाएं, आप बहुत साफ चीजें करने में सक्षम हैं। उदाहरण के लिए:

data Data = Data { member :: Int } 

-- The "getter" of the member variable 
getMember :: Data -> Int 
getMember d = member d 

-- The "setter" or more accurately "updater" of the member variable 
setMember :: Data -> Int -> Data 
setMember d m = d { member = m } 

memberLens :: (Data -> Int, Data -> Int -> Data) 
memberLens = (getMember, setMember) 

लेंस को लागू करने के कई तरीके हैं; इस पाठ के लिए, मान लें कि एक लेंस उपरोक्त की तरह है:

type Lens a b = (a -> b, a -> b -> a) 

आईई। यह किसी प्रकार के a के लिए गेटर और एक सेटटर का संयोजन है जिसमें b प्रकार का क्षेत्र है, इसलिए ऊपर memberLensLens Data Int होगा। यह हमें क्या करने देता है?

ठीक है, पहले दो सरल कार्यों है कि एक लेंस से getters और setters निकालने बनाते हैं:

getL :: Lens a b -> a -> b 
getL (getter, setter) = getter 

setL :: Lens a b -> a -> b -> a 
setL (getter, setter) = setter 

अब, हम सामान से अधिक सार संक्षेप शुरू कर सकते हैं। आइए स्थिति को फिर से लें, कि हम "दो कहानियों को गहरा" मान संशोधित करना चाहते हैं। हम एक और लेंस के साथ एक डेटा संरचना जोड़ें:

data Foo = Foo { subData :: Data } 

subDataLens :: Lens Foo Data 
subDataLens = (subData, \ f s -> f { subData = s }) -- short lens definition 

अब, चलो एक समारोह है कि दो लेंस तैयार करता जोड़ें:

(#) :: Lens a b -> Lens b c -> Lens a c 
(#) (getter1, setter1) (getter2, setter2) = 
    (getter2 . getter1, combinedSetter) 
    where 
     combinedSetter a x = 
     let oldInner = getter1 a 
      newInner = setter2 oldInner x 
     in setter1 a newInner 

कोड तरह का जल्दी से लिखा है, लेकिन मुझे लगता है यह स्पष्ट है कि वह क्या करता : गेटर्स बस रचित हैं; आपको आंतरिक डेटा मान मिलता है, और फिर आप अपना क्षेत्र पढ़ते हैं। सेटर, जब इसे a पर x के नए आंतरिक फ़ील्ड मान के साथ कुछ मान बदलने के लिए माना जाता है, तो पहले पुरानी आंतरिक डेटा संरचना को पुनर्प्राप्त करता है, इसके आंतरिक क्षेत्र को सेट करता है, और फिर नई आंतरिक डेटा संरचना के साथ बाहरी डेटा संरचना को अपडेट करता है।

increment :: Lens a Int -> a -> a 
increment l a = setL l a (getL l a + 1) 

हम इस कोड है, तो यह स्पष्ट हो जाता कि वह क्या करता:

अब, चलो एक समारोह है कि बस एक लेंस के मूल्य वृद्धि कर देता है बनाते हैं,

d = Data 3 
print $ increment memberLens d -- Prints "Data 4", the inner field is updated. 

अब क्योंकि हम लेंस बना सकता है, हम भी ऐसा कर सकते हैं:

f = Foo (Data 5) 
print $ increment (subDataLens#memberLens) f 
-- Prints "Foo (Data 6)", the innermost field is updated. 

क्या लेंस संकुल के सभी w करने के लिए अनिवार्य रूप से है लेंस की इस अवधारणा को रैप करें - एक "सेटर" और "गेटर" का समूह एक साफ पैकेज में है जो उन्हें उपयोग करना आसान बनाता है। एक विशेष लेंस कार्यान्वयन में, एक लिखने में सक्षम हो जाएगा:

with (Foo (Data 5)) $ do 
    subDataLens . memberLens $= 7 

तो, आप बहुत कोड के सी संस्करण के करीब; डेटा संरचनाओं के पेड़ में नेस्टेड मानों को संशोधित करना बहुत आसान हो जाता है।

लेंस इस से कुछ भी अधिक नहीं हैं: कुछ डेटा के हिस्सों को संशोधित करने का एक आसान तरीका। क्योंकि उनके कारण कुछ अवधारणाओं के कारण यह बहुत आसान हो जाता है, वे उन परिस्थितियों में व्यापक उपयोग देखते हैं जहां आपके पास डेटा संरचनाओं के विशाल सेट होते हैं जिन्हें विभिन्न तरीकों से एक-दूसरे से बातचीत करना पड़ता है।

लेंस के पेशेवरों और विपक्ष के लिए, a recent question here on SO देखें।

+2

आपके उत्तर में एक महत्वपूर्ण बात यह है कि लेंस * प्रथम श्रेणी * है, इसलिए आप उनसे अन्य अवशोषण बना सकते हैं। अंतर्निहित रिकॉर्ड वाक्यविन्यास उस संबंध में विफल रहता है। – jberryman

+2

इसके अलावा, मैंने लेंस पर एक ब्लॉग पोस्ट लिखा जो ओपी के लिए उपयोगी हो सकता है: http://www.haskellforall.com/2012/01/haskell-for-mainstream-programmers_28.html –

12

लेंस डेटा संरचनाओं को एक समान, रचनात्मक तरीके से संपादित करने के सुविधाजनक तरीके प्रदान करते हैं।

कई कार्यक्रमों निम्नलिखित कार्यों के आसपास निर्माण कर रहे हैं:

  • (संभवत: नेस्ट) डेटा संरचना के एक घटक को देखने
  • (संभवतः नेस्ट) डाटा संरचनाओं

लेंस प्रदान की

  • अद्यतन करने के खेतों संरचनाओं को देखने और संपादित करने के लिए भाषा समर्थन इस तरह से सुनिश्चित करता है कि आपके संपादन सुसंगत हैं; कि संपादन आसानी से रचित किया जा सकता है; और संरचना के कुछ हिस्सों को अद्यतन करने के लिए, एक ही संरचना को संरचना के कुछ हिस्सों को देखने के लिए उपयोग किया जा सकता है।

    लेंस इस प्रकार संरचनाओं पर विचारों से कार्यक्रम लिखना आसान बनाता है; और उन संरचनाओं के लिए संरचनाओं (और संपादकों) पर संरचनाओं से वापस। वे रिकॉर्ड एक्सेसर्स और सेटर्स की बहुत सारी गड़बड़ी को साफ करते हैं।

    पिएर्स एट अल। लोकप्रिय लेंस, उदा। उनके Quotient Lenses paper में, और हास्केल के लिए कार्यान्वयन अब व्यापक रूप से उपयोग किए जाते हैं (उदा। fclabels और डेटा-एक्सेसर्स)।

    ठोस उपयोग के मामलों के लिए, पर विचार करें:

    • ग्राफिकल यूजर इंटरफेस, जहां एक उपयोगकर्ता एक संरचित तरीके से जानकारी संपादित कर रहा है
    • पारसर्स और सुंदर प्रिंटर
    • compilers
    • समन्वयन करने को अद्यतन करने के डेटा संरचनाओं
    • डेटाबेस और स्कीमा

    और कई अन्य परिस्थितियां जहां आपके पास दुनिया का डेटा स्ट्रक्चर मॉडल है, और उस डेटा पर एक संपादन योग्य दृश्य है।

  • 6

    एक अतिरिक्त नोट के रूप में इसे अक्सर अनदेखा किया जाता है कि लेंस "क्षेत्र पहुंच और अद्यतन" की एक बहुत ही सामान्य धारणा को लागू करते हैं। लेंस को सभी प्रकार की चीजों के लिए लिखा जा सकता है, जिसमें फ़ंक्शन-जैसी ऑब्जेक्ट्स शामिल हैं।

    at :: (Eq a) => a -> Lens (a -> b) b 
    

    at का उपयोग करते हुए आप वास्तव में पहुँच सकते हैं और पहले तर्क के आधार पर कई तर्क के साथ कार्यों में हेरफेर कर सकते हैं: यह इस की सराहना करते हैं, तो मैं आपको लेंस की शक्ति का एक उदाहरण दिखाने देने के लिए अमूर्त सोच का एक सा की आवश्यकता है। बस ध्यान रखें कि Lens एक श्रेणी है। यह स्थानीय रूप से समायोजित कार्यों या अन्य चीजों के लिए एक बहुत उपयोगी मुहावरे है।

    तुम भी गुण या वैकल्पिक अभ्यावेदन द्वारा डेटा का उपयोग कर सकते हैं:

    polar :: (Floating a, RealFloat a) => Lens (Complex a) (a, a) 
    mag :: (RealFloat a) => Lens (Complex a) a 
    

    आप किसी फूरियर-बदल संकेत और एक बहुत अधिक के अलग-अलग बैंड का उपयोग करने के लेंस लेखन जा सकते हैं।

    संबंधित मुद्दे