আপনি কীভাবে জানবেন যে কখন ভাঁজ-বাম ব্যবহার করবেন এবং কখন ভাঁজ-ডান ব্যবহার করবেন?


100

আমি জানি যে ভাঁজ-বাম বাম-ঝোঁক গাছ উত্পাদন করে এবং ভাঁজ-ডান ডান-ঝোঁক গাছ উত্পন্ন করে, তবে যখন আমি একটি ভাঁজ পৌঁছায় তখন কখনও কখনও নিজেকে মাথাব্যথা-প্ররোচিত চিন্তায় ডুবিয়ে দিতে দেখি যে কোন ধরণের ভাঁজ নির্ধারণ করার চেষ্টা করছে উপযুক্ত। আমি সাধারণত পুরো সমস্যাটিকে আনন্ডাইন্ড করে ফোল্ড ফাংশনটি বাস্তবায়িত করি যা এটি আমার সমস্যার ক্ষেত্রে প্রযোজ্য।

সুতরাং আমি যা জানতে চাই তা হ'ল:

  • বামে বা ডানদিকে ভাঁজ করতে হবে তা নির্ধারণের জন্য থাম্বের কিছু নিয়ম কী?
  • আমি যে সমস্যার মুখোমুখি হচ্ছি তাতে কীভাবে আমি কোন ধরণের ভাঁজ ব্যবহার করব তা দ্রুত কীভাবে সিদ্ধান্ত নিতে পারি?

সেখানে একটি উদাহরণ দ্বারা উদাহরণ Scala একটি ফাংশন বলা চেপ্টা যা একটি একক তালিকায় উপাদান তালিকার একটি তালিকা concatenates লিখতে একটি ভাঁজ ব্যবহারের (পিডিএফ)। সেক্ষেত্রে একটি ডান ভাঁজ হ'ল যথাযথ পছন্দ (তালিকাগুলি যেভাবে বোঝানো হয়েছে তা দেওয়া হয়েছে), তবে এই সিদ্ধান্তে পৌঁছতে আমাকে কিছুটা ভাবতে হয়েছিল।

যেহেতু ভাঁজ (ফাংশনাল) প্রোগ্রামিংয়ে এমন একটি সাধারণ ক্রিয়া, তাই আমি দ্রুত এবং আত্মবিশ্বাসের সাথে এই ধরণের সিদ্ধান্ত নিতে সক্ষম হতে চাই। সুতরাং ... কোন টিপস?



4
এই প্রশ্নটি তার চেয়ে বেশি সাধারণ, যা বিশেষত হাস্কেলকে নিয়ে ছিল। প্রশ্নের উত্তরের ক্ষেত্রে অলসতা একটি বড় পার্থক্য তৈরি করে।
ক্রিস কনওয়ে

উহু. অদ্ভুত, একরকম আমি ভেবেছিলাম আমি এই প্রশ্নে একটি হাস্কেল ট্যাগ দেখেছি, তবে আমি অনুমান করি না ...
প্রথম

উত্তর:


108

আপনি একটি ভাঁজকে একটি ইনফিক্স অপারেটর স্বরলিপিতে স্থানান্তর করতে পারেন (এর মধ্যে লেখার জন্য):

এই উদাহরণস্বরূপ ফাংশন ব্যবহার করে ভাঁজ x

fold x [A, B, C, D]

এইভাবে সমান

A x B x C x D

এখন আপনাকে কেবল আপনার অপারেটরের সাহচর্য সম্পর্কে যুক্তি করতে হবে (বন্ধনী স্থাপনের মাধ্যমে!)।

আপনার যদি বাম-সহযোগী অপারেটর থাকে তবে আপনি এটির মতো প্রথম বন্ধনী সেট করবেন

((A x B) x C) x D

এখানে, আপনি একটি বাম ভাঁজ ব্যবহার করুন । উদাহরণ (হ্যাশেল স্টাইলের সিউডোকোড)

foldl (-) [1, 2, 3] == (1 - 2) - 3 == 1 - 2 - 3 // - is left-associative

যদি আপনার অপারেটরটি ডান-অ্যাসোসিয়েটিভ ( ডান ভাঁজ ) হয় তবে প্রথম বন্ধনী সেট করা হবে:

A x (B x (C x D))

উদাহরণ: কনস অপারেটর

foldr (:) [] [1, 2, 3] == 1 : (2 : (3 : [])) == 1 : 2 : 3 : [] == [1, 2, 3]

সাধারণভাবে, গাণিতিক অপারেটরগুলি (বেশিরভাগ অপারেটর) বাম-সহযোগী হয়, তাই foldlএটি আরও ব্যাপক। তবে অন্যান্য ক্ষেত্রে ইনফিক্স নোটেশন + প্রথম বন্ধনী বেশ কার্যকর।


6
ঠিক আছে, আপনি যা বর্ণনা করেছেন তা আসলে foldl1এবং foldr1হাস্কেলের ( foldlএবং foldrএকটি বাহ্যিক প্রাথমিক মান গ্রহণ করুন), এবং হাস্কেলের "কনস" বলা হয় (:)না (::), তবে অন্যথায় এটি সঠিক। আপনি যোগ করতে যে Haskell, অতিরিক্ত একটি উপলব্ধ চাইতে পারেন foldl'/ foldl1'যার কঠোর রূপের হয় foldl/ foldl1, কারণ অলস গাণিতিক সবসময় কাম্য নয়।
প্রশংসাপত্র

দুঃখিত, আমি ভেবেছিলাম আমি এই প্রশ্নের একটি "হাস্কেল" ট্যাগ দেখেছি, কিন্তু এটি সেখানে নেই। আমার মন্তব্যটি সত্যিকার অর্থে
এতটা অর্থবোধ করে

আপনি এটি দেখতে পেয়েছেন এটি "হ্যাশেল স্টাইলের সিউডোকোড"। :)
হাসছে_মান

ভাড়ার মধ্যে পার্থক্যের সাথে সম্পর্কিত আমি কখনও দেখেছি সেরা উত্তর।
আলেক্সাউন্ডস

61

অলিন শাওয়ারগুলি "ভাঁজটি মৌলিক তালিকার পুনরাবৃত্তকারী" এবং "ফোল্ডারটি মৌলিক তালিকা পুনরাবৃত্তি অপারেটর" বলে তাদের পার্থক্য করে। আপনি কীভাবে ভাঁজ কাজ করে তা দেখুন:

((1 + 2) + 3) + 4

আপনি সঞ্চয়ী দেখতে পাচ্ছেন (যেমন একটি লেজ-পুনরাবৃত্ত পুনরাবৃত্তির মতো) তৈরি হচ্ছে। বিপরীতে, ফোল্ডার এগিয়ে:

1 + (2 + (3 + 4))

যেখানে আপনি বেস কেস 4 এ ট্র্যাভারসাল দেখতে পাবেন এবং সেখান থেকে ফলাফল তৈরি করতে পারেন।

সুতরাং আমি থাম্বের একটি নিয়ম পোষ্ট করছি: যদি এটি তালিকার পুনরাবৃত্তির মতো মনে হয়, তবে এটি লেজ-পুনরাবৃত্ত আকারে লিখতে সহজ হবে, ভাঁজ হ'ল উপায়।

তবে প্রকৃতপক্ষে এটি সম্ভবত আপনি যে অপারেটরগুলি ব্যবহার করছেন তাদের সাহসীতার থেকে সবচেয়ে স্পষ্ট হবে। যদি তারা বাম-সহযোগী হয় তবে ভাঁজ ব্যবহার করুন। সেগুলি ডান-সহযোগী হলে ফোল্ডারটি ব্যবহার করুন।


29

অন্যান্য পোস্টারগুলি ভাল উত্তর দিয়েছে এবং তারা ইতিমধ্যে যা বলেছে তার পুনরাবৃত্তি করব না। আপনার প্রশ্নে যেমন আপনি একটি স্কেলার উদাহরণ দিয়েছেন, আমি একটি স্কালার নির্দিষ্ট উদাহরণ দেব। ট্র্যাকস যেমন ইতিমধ্যে বলেছে, স্ট্যাক ফ্রেমগুলি foldRightসংরক্ষণ করা দরকার n-1, nআপনার তালিকার দৈর্ঘ্য কোথায় এবং এটি সহজেই একটি স্ট্যাকের ওভারফ্লো করতে পারে - এমনকি পুচ্ছ পুনরাবৃত্তিও আপনাকে এ থেকে বাঁচাতে পারে না।

List(1,2,3).foldRight(0)(_ + _)হ্রাস করবে:

1 + List(2,3).foldRight(0)(_ + _)        // first stack frame
    2 + List(3).foldRight(0)(_ + _)      // second stack frame
        3 + 0                            // third stack frame 
// (I don't remember if the JVM allocates space 
// on the stack for the third frame as well)

যখন List(1,2,3).foldLeft(0)(_ + _)হ্রাস হবে:

(((0 + 1) + 2) + 3)

যা পুনরুক্তি হিসাবে গণনা করা যেতে পারে, বাস্তবায়নের ক্ষেত্রেList সম্পন্ন হিসাবে ।

স্কালার হিসাবে কঠোরভাবে মূল্যায়ন করা ভাষায়, একটি foldRightসহজেই বড় তালিকাগুলির জন্য স্ট্যাকটি উড়িয়ে দিতে পারে, যখন foldLeftতা হয় না।

উদাহরণ:

scala> List.range(1, 10000).foldLeft(0)(_ + _)
res1: Int = 49995000

scala> List.range(1, 10000).foldRight(0)(_ + _)
java.lang.StackOverflowError
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRight(List.scala:1081)
        at scala.List.foldRig...

আমার থাম্বের নিয়মটি তাই - অপারেটরদের জন্য যাদের কোনও নির্দিষ্ট সাহসীতা নেই, সর্বদা ব্যবহার করুন foldLeft, কমপক্ষে স্কালায়। অন্যথায়, উত্তরগুলিতে দেওয়া অন্যান্য পরামর্শ সহ যান;)।


13
এটি আগে সত্য ছিল তবে স্কালার বর্তমান সংস্করণগুলিতে তালিকার বিপরীত অনুলিপিতে ফোল্ডলাইট প্রয়োগ করতে ফোল্ডরাইট পরিবর্তন করা হয়েছে। উদাহরণস্বরূপ, 2.10.3 এ, github.com/scala/scala/blob/v2.10.3/src/library/scala/… । মনে হচ্ছে এই পরিবর্তনটি 2013 এর প্রথম দিকে করা হয়েছিল - github.com/scala/scala/commit/…
ধ্রুব কাপুর

4

এটি লক্ষণীয়ও (এবং আমি বুঝতে পারি এটি স্পষ্টভাবে কিছুটা বলে দিচ্ছে), একজন পরিবহনের অপারেটরের ক্ষেত্রে উভয়ই বেশ সমতুল্য। এই পরিস্থিতিতে একটি ভাঁজ ভাল পছন্দ হতে পারে:

ভাঁজ: (((1 + 2) + 3) + 4)প্রতিটি ক্রিয়াকলাপ গণনা করতে এবং জমে থাকা মানকে এগিয়ে নিয়ে যেতে পারে

ফোল্ডার: গণনার (1 + (2 + (3 + 4)))জন্য 1 + ?এবং তার 2 + ?আগে খোলার জন্য একটি স্ট্যাক ফ্রেম প্রয়োজন 3 + 4, তারপরে এটি ফিরে যেতে হবে এবং প্রতিটিটির জন্য গণনা করা দরকার।

আমি কার্যকরী ভাষা বা সংকলক অপ্টিমাইজেশনের বিশেষজ্ঞের পক্ষে যথেষ্ট তা বলতে পারি না যে এটি আসলে কোনও পার্থক্য করবে কিনা তবে অবশ্যই কমিউটিভেটিভ অপারেটরগুলির সাথে একটি ভাঁজ ব্যবহার করা পরিষ্কার মনে হয়।


4
অতিরিক্ত স্ট্যাক ফ্রেম অবশ্যই বড় তালিকাগুলির জন্য একটি পার্থক্য আনবে। যদি আপনার স্ট্যাক ফ্রেমগুলি প্রসেসরের ক্যাশের আকারের চেয়ে বেশি হয়, তবে আপনার ক্যাশে-মিসগুলি কর্মক্ষমতাকে প্রভাবিত করবে। তালিকাকে দ্বিগুণভাবে সংযুক্ত না করা হলে ফোল্ডারটিকে একটি পুচ্ছ-পুনরাবৃত্ত ফাংশন তৈরি করা এক ধরণের শক্ত কাজ, তাই যদি না করার কারণ না থাকে তবে আপনার ভাঁজটি ব্যবহার করা উচিত।
এ লেভি

4
হাস্কেলের অলস প্রকৃতি এই বিশ্লেষণকে বিশৃঙ্খলা করে। যদি ফাংশনটি ভাঁজ করা হচ্ছে দ্বিতীয় প্যারামিটারে অ-কঠোর হয়, তবে foldrতার চেয়ে খুব ভাল দক্ষ হতে পারে foldlএবং কোনও অতিরিক্ত স্ট্যাক ফ্রেমের প্রয়োজন হবে না।
প্রশংসাপত্র

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