কেউ হাস্কেলের ট্র্যাভার্স ফাংশনটি ব্যাখ্যা করতে পারে?


99

আমি চেষ্টা করছি এবং traverseএখান থেকে ফাংশন ছাঁটাই ব্যর্থ Data.Traversable। আমি এর বিন্দু দেখতে অক্ষম। যেহেতু আমি একটি অত্যাবশ্যকীয় পটভূমি থেকে এসেছি, কেউ দয়া করে একটি অপরিহার্য লুপের শর্তে আমাকে এটি ব্যাখ্যা করতে পারেন? সিউডো-কোড অনেক প্রশংসা হবে। ধন্যবাদ


4
ধাপে ধাপে ট্র্যাভার্সের ধারণাটি তৈরি করার সাথে সাথে ইটারেটর প্যাটার্নটির সারমর্মটি নিবন্ধটি সহায়ক হতে পারে। কিছু উন্নত ধারণাটি যদিও উপস্থিত রয়েছে
জ্যাকি

উত্তর:


121

traverseএকই হিসাবে fmap, আপনি ডেটা কাঠামো পুনর্নির্মাণের সময় এটি আপনাকে প্রভাব চালানোর অনুমতি দেয়।

Data.Traversableডকুমেন্টেশন থেকে উদাহরণটি একবার দেখুন ।

 data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a)

Functorদৃষ্টান্ত Treeহবে:

instance Functor Tree where
  fmap f Empty        = Empty
  fmap f (Leaf x)     = Leaf (f x)
  fmap f (Node l k r) = Node (fmap f l) (f k) (fmap f r)

এটি fপ্রতিটি গাছের মূল্য প্রয়োগ করে পুরো গাছটিকে পুনর্নির্মাণ করে।

instance Traversable Tree where
    traverse f Empty        = pure Empty
    traverse f (Leaf x)     = Leaf <$> f x
    traverse f (Node l k r) = Node <$> traverse f l <*> f k <*> traverse f r

Traversableছাড়া কনস্ট্রাকটর আবেদন শৈলী বলা হয় উদাহরণস্বরূপ, প্রায় একই। এর অর্থ গাছটি পুনর্নির্মাণের সময় আমরা (পার্শ্ব) প্রভাব ফেলতে পারি। প্রয়োগকারী প্রায় সোনাডের সমান, প্রভাবগুলি পূর্ববর্তী ফলাফলের উপর নির্ভর করে না except এই উদাহরণে এর অর্থ হ'ল উদাহরণস্বরূপ বাম শাখাটি পুনর্নির্মাণের ফলাফলের উপর নির্ভর করে আপনি নোডের ডান শাখায় কিছু আলাদা করতে পারেন নি।

TraversableReasonsতিহাসিক কারণে, ক্লাসে traverseডাকা একটি monadic সংস্করণ রয়েছে mapM। সমস্ত উদ্দেশ্য এবং উদ্দেশ্যগুলির mapMজন্য একই traverse- এটি পৃথক পদ্ধতি হিসাবে বিদ্যমান কারণ Applicativeকেবল পরে এটির একটি সুপারক্লাস হয়ে যায় Monad

আপনি যদি এটি অপরিষ্কার ভাষায় প্রয়োগ করেন তবে পার্শ্ব প্রতিক্রিয়া প্রতিরোধের কোনও উপায় না fmapহওয়ায় এটি একই রকম হবে traverse। আপনি এটিকে একটি লুপ হিসাবে বাস্তবায়ন করতে পারবেন না, কারণ আপনাকে অবশ্যই আপনার ডেটা কাঠামোটিকে পুনরাবৃত্তভাবে অতিক্রম করতে হবে। আমি জাভাস্ক্রিপ্টে এটি কীভাবে করব তা এখানে একটি ছোট উদাহরণ রয়েছে:

Node.prototype.traverse = function (f) {
  return new Node(this.l.traverse(f), f(this.k), this.r.traverse(f));
}

এটিকে এরূপে প্রয়োগ করা ভাষা আপনাকে যে পরিমাণ প্রভাব দেয় তা সীমাবদ্ধ করে। আপনি যদি অ-নির্ধারণবাদ চান (যা প্রয়োগকারী মডেলগুলির তালিকার উদাহরণ) এবং আপনার ভাষায় এটি অন্তর্নির্মিত না থাকে, আপনি ভাগ্যের বাইরে।


11
'প্রভাব' শব্দটির অর্থ কী?
মিসিংফ্যাক্টর

25
@ মিসিংফাক্টর: এর অর্থ একটি এর কাঠামোগত তথ্য Functor, যে অংশটি প্যারামেট্রিক নয়। রাষ্ট্র মান Stateএ, ব্যর্থতা Maybeএবং Either, উপাদানের মধ্যে সংখ্যা [], এবং অবশ্যই অবাধ বহিরাগত পার্শ্ব প্রতিক্রিয়া IO। আমি এটিকে জেনেরিক শব্দ হিসাবে যত্ন নিই না ( Monoid"খালি" এবং "সংযোজন" ব্যবহার করে ফাংশনগুলির মতো ধারণাটি প্রথমে শব্দের চেয়ে বেশি সাধারণ) তবে এটি মোটামুটি সাধারণ এবং উদ্দেশ্যটি যথেষ্টভাবে কার্যকর করে।
সিএ ম্যাকক্যান

@ সিএ ম্যাকক্যান: বুঝেছি উত্তর করার জন্য ধন্যবাদ!
মিসিংফ্যাক্টর

4
"আমি নিশ্চিত যে আপনার এটি করার কথা নয় [...]।" অবশ্যই না - এটি apপূর্ববর্তী ফলাফলের উপর নির্ভর করে প্রভাব তৈরি করার মতোই বাজে । আমি সেই মন্তব্যটি সেই অনুসারে বলেছি।
ডুবলড

4
"প্রয়োগকারী প্রায় মনাদ হিসাবে একই, প্রভাবগুলি পূর্ববর্তী ফলাফলের উপর নির্ভর করতে পারে except" ... এই লাইনটি দিয়ে আমার জন্য প্রচুর স্টাফ ক্লিক করা হয়েছে, ধন্যবাদ!
আগাম

58

traverseএকটি ভিতরে কিছু সক্রিয় Traversableএকটি মধ্যে Traversableকিছু "ভিতরে" একটি এর Applicative, একটি ফাংশন যে দেওয়া তোলে Applicativeজিনিস গুলি করে।

এর Maybeহিসাবে Applicativeএবং তালিকা হিসাবে ব্যবহার করা যাক Traversable। প্রথমে আমাদের ট্রান্সফর্মেশন ফাংশনটি দরকার:

half x = if even x then Just (x `div` 2) else Nothing

সুতরাং একটি সংখ্যা যদি সমান হয় তবে আমরা এর অর্ধেক (এ এর অভ্যন্তরে Just) পাই, অন্যথায় আমরা পাই Nothing। যদি সবকিছু "ভাল" হয় তবে এটি এর মতো দেখাচ্ছে:

traverse half [2,4..10]
--Just [1,2,3,4,5]

তবে ...

traverse half [1..10]
-- Nothing

কারণটি হ'ল <*>ফলটি তৈরি করতে ফলাফলটি ব্যবহার করা হয় এবং যখন যুক্তিগুলির মধ্যে একটি হয় তখন Nothingআমরা Nothingফিরে আসি।

আরেকটি উদাহরণ:

rep x = replicate x x

এই ফাংশনটি xসামগ্রী সহ দৈর্ঘ্যের একটি তালিকা উত্পন্ন করে x, যেমন rep 3= [3,3,3]। এর ফলাফল কী traverse rep [1..3]?

আমরা আংশিক ফলাফল পেতে [1], [2,2]এবং [3,3,3]ব্যবহার rep। এখন তালিকার শব্দার্থবিদ্যা যেমন Applicativesহয় "সব সমন্বয় নেওয়া", যেমন (+) <$> [10,20] <*> [3,4]হয় [13,14,23,24]

এর "সমস্ত সংমিশ্রণ" [1]এবং [2,2]দুটি বার [1,2]। দুটি বারের সমস্ত সংমিশ্রণ [1,2]এবং [3,3,3]ছয়বার [1,2,3]। তাহলে আমাদের আছে:

traverse rep [1..3]
--[[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3]]

4
আপনি শেষ ফলাফল আমাকে এই মনে করিয়ে দেয় ।
hugomg

4
@ মিসিংনো: হ্যাঁ, তারা মিস করেছেfac n = length $ traverse rep [1..n]
ল্যান্ডেই

4
আসলে, এটি সেখানে "তালিকা-এনকোডিং-প্রোগ্রামার" এর অধীনে (তবে তালিকার উপলব্ধিগুলি ব্যবহার করে)। এই ওয়েবসাইটটি বিস্তৃত :)
hugomg

4
@ মিসিংনো: এইচএম, এটি হুবহু এক নয় ... দুজনেই তালিকার মনডের কার্তেসিয়ান পণ্য আচরণের উপর নির্ভর করছে, তবে সাইটটি একবারে দুটি ব্যবহার করে, তাই এটি liftA2 (,)আরও জেনেরিক ফর্মটি ব্যবহারের চেয়ে বেশি করার মতো traverse
সিএ ম্যাকক্যান

42

আমি মনে করি এটি নীচের হিসাবে সংজ্ঞায়িত করা যেতে পারে sequenceA, শর্তাবলী বোঝা সবচেয়ে সহজ traverse

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f

sequenceA বাম থেকে ডানে একটি কাঠামোর উপাদানগুলিকে এক সাথে সিক্যুয়েন্স করে ফলাফল সমান আকৃতির একটি কাঠামো ফিরিয়ে আনতে।

sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
sequenceA = traverse id

আপনি sequenceAদুটি ফান্টারের ক্রমকে বিপরীত হিসাবেও ভাবতে পারেন , উদাহরণস্বরূপ কর্মের একটি তালিকা থেকে ফলাফলের তালিকা ফিরিয়ে দেওয়া কোনও ক্রিয়ায়।

সুতরাং traverseকিছু কাঠামো লাগে এবং কাঠামোর fপ্রতিটি উপাদানকে কিছু আবেদনকারীতে রূপান্তরিত করার জন্য প্রয়োগ করা হয়, এটি ফলস্বরূপ একই আকৃতির একটি কাঠামো ফিরিয়ে দিয়ে বাম থেকে ডানে সেই আবেদনকারীদের প্রভাবগুলির ক্রমগুলি সিকোয়েন্স করে।

আপনি এটির সাথে তুলনাও করতে পারেন Foldable, যা সম্পর্কিত ফাংশনটি সংজ্ঞায়িত করে traverse_

traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()

আপনি দেখতে পারেন সুতরাং যে মধ্যবর্তী কী পার্থক্য Foldableএবং Traversableযে আধুনিক, যেহেতু সাবেক কিছু অন্যান্য মান মধ্যে ফলাফলের ভাঁজ করতে হবে আপনি কাঠামোর আকৃতি সংরক্ষণ করার মঞ্জুরি দেয়।


এর ব্যবহারের একটি সাধারণ উদাহরণটি একটি তালিকাটিকে ট্র্যাভারেবল কাঠামো এবং IOপ্রয়োগকারী হিসাবে ব্যবহার করছে:

λ> import Data.Traversable
λ> let qs = ["name", "quest", "favorite color"]
λ> traverse (\thing -> putStrLn ("What is your " ++ thing ++ "?") *> getLine) qs
What is your name?
Sir Lancelot
What is your quest?
to seek the holy grail
What is your favorite color?
blue
["Sir Lancelot","to seek the holy grail","blue"]

যদিও এই উদাহরণটি বরং উদ্বেগজনক, traverseঅন্য ধরণের পাত্রে ব্যবহার করা বা অন্যান্য প্রয়োগকারী ব্যবহার করার সময় জিনিসগুলি আরও আকর্ষণীয় হয়ে ওঠে ।


সুতরাং ট্র্যাভার্সটি কেবল ম্যাপএমের আরও সাধারণ ফর্ম? আসলে, sequenceA . fmapতালিকাগুলি সমান sequence . mapনয় কি?
রাসেল

'সাইডিং এফেক্টস সিকোয়েন্সিং' বলতে কী বোঝ? আপনার উত্তরে 'পার্শ্ব প্রতিক্রিয়া' কী - আমি কেবল ভেবেছিলাম যে পার্শ্ব প্রতিক্রিয়াগুলি কেবল মনদেটেই সম্ভব। শ্রদ্ধা।
মেরেক

4
@ মারেক "আমি কেবল ভেবেছিলাম পার্শ্ব প্রতিক্রিয়া কেবলমাত্র মনডের মধ্যেই সম্ভব" - সংযোগটি এর চেয়ে অনেক লুজ: (1) এই IO ধরণের পার্শ্ব প্রতিক্রিয়া প্রকাশ করতে ব্যবহার করা যেতে পারে; (২) IOএকটি মোনাড হতে দেখা যায় যা খুব সুবিধাজনক বলে প্রমাণিত হয়। মনডগুলি মূলত পার্শ্ব প্রতিক্রিয়াগুলির সাথে যুক্ত নয়। এটিও লক্ষ করা উচিত যে "ইফেক্ট" এর একটি অর্থ রয়েছে যা এর স্বাভাবিক অর্থে "পার্শ্ব প্রতিক্রিয়া" এর চেয়ে বিস্তৃত - যার মধ্যে খাঁটি গণনা অন্তর্ভুক্ত থাকে। এই শেষ পয়েন্টে, "কার্যকর" অর্থ কী বোঝায় তাও দেখুন
ডুপলড

(যাইহোক, @ হ্যামার, উপরের মন্তব্যে বর্ণিত কারণগুলির কারণে আমি এই উত্তরে "পার্শ্ব প্রতিক্রিয়া" "পরিবর্তিত" করার স্বাধীনতা নিয়েছি।)
ডুপলড

17

এটি একধরণের মত fmap, আপনি ম্যাপার ফাংশনের অভ্যন্তরে প্রভাব চালাতে পারেন যা ফলাফলের ধরণের পরিবর্তনও করে।

একটি ডাটাবেসের মধ্যে ব্যবহারকারীর ID- র প্রতিনিধিত্বমূলক পূর্ণসংখ্যার একটি তালিকা কল্পনা: [1, 2, 3]। আপনি যদি fmapএই ব্যবহারকারীর আইডি ব্যবহারকারীর নামতে চান তবে আপনি একটি traditionalতিহ্যবাহী ব্যবহার করতে পারবেন না fmap, কারণ ফাংশনের ভিতরে আপনাকে ব্যবহারকারীর নাম পড়ার জন্য ডাটাবেস অ্যাক্সেস করতে হবে (যার ক্ষেত্রে একটি প্রভাব প্রয়োজন - এই ক্ষেত্রে, IOমোনাড ব্যবহার করে )।

এর স্বাক্ষর traverseহ'ল:

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

এর সাহায্যে traverseআপনি প্রভাব ফেলতে পারেন, সুতরাং, ব্যবহারকারীর আইডিতে ব্যবহারকারীদের আইডি ম্যাপ করার জন্য আপনার কোডটি দেখতে এই রকম দেখাচ্ছে:

mapUserIDsToUsernames :: (Num -> IO String) -> [Num] -> IO [String]
mapUserIDsToUsernames fn ids = traverse fn ids

এখানে একটি ফাংশনও রয়েছে mapM:

mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)

কোনও ব্যবহারের mapMসাথে প্রতিস্থাপন করা যেতে পারে traverse, তবে অন্যভাবে নয়। mapMশুধুমাত্র মনডদের জন্য কাজ করে, যেখানে traverseআরও জেনেরিক।

আপনি যদি কেবল কোনও প্রভাব অর্জন করতে চান এবং কোনও দরকারী মান না ফিরিয়ে দিতে চান তবে এই ফাংশনের বিভিন্ন সংস্করণ রয়েছে traverse_এবং mapM_উভয়ই ফাংশন থেকে রিটার্ন মানটিকে অগ্রাহ্য করে এবং কিছুটা দ্রুত।



7

traverse হয় লুপ। এর বাস্তবায়ন নির্ভর করে ডেটা স্ট্রাকচারের উপর নির্ভর করে। এটা একটা তালিকা, গাছ, হতে পারে Maybe, Seq(uence), অথবা কিছু একটি জন্য-লুপ বা recursive ফাংশন ভালো কিছু মাধ্যমে ভেদকরেখার হচ্ছে একটি জেনেরিক পথ রয়েছে। একটি অ্যারে একটি ফোর লুপ, একটি তালিকা যখন একটি লুপ, একটি গাছ হয় পুনরাবৃত্তি কিছু বা একটি সময় লুপ সঙ্গে একটি স্ট্যাকের সংমিশ্রণ হবে; তবে কার্যকরী ভাষায় আপনার এই জটিল লুপ কমান্ডের দরকার নেই: আপনি লুপের অভ্যন্তরীণ অংশটি (একটি ফাংশনের আকারে) আরও বেশি সরাসরি পদ্ধতিতে এবং কম ভার্বোজের সাথে ডেটা স্ট্রাকচারের সাথে একত্রিত করেন।

সঙ্গে Traversabletypeclass, আপনি সম্ভবত আপনার আলগোরিদিম আরো স্বাধীন ও বহুমুখী লিখতে পারেন। তবে আমার অভিজ্ঞতা বলে, এটি Traversableসাধারণত বিদ্যমান ডাটা স্ট্রাকচারগুলিতে কেবল আলগোরিদিমগুলিকে আঠালো করতে ব্যবহৃত হয়। যোগ্য, বিভিন্ন ডেটাটাইপগুলির জন্য একই রকম ফাংশন লেখার দরকার নেই এটি বেশ চমৎকার।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.