আবশ্যক বিরতি বিবৃতি এবং অন্যান্য লুপ চেকের কার্যকরী সমতুল্যগুলি কী কী?


36

ধরা যাক, আমি নীচের যুক্তি দিয়েছি। কীভাবে এটি ফাংশনাল প্রোগ্রামিংয়ে লিখবেন?

    public int doSomeCalc(int[] array)
    {
        int answer = 0;
        if(array!=null)
        {
            for(int e: array)
            {
                answer += e;
                if(answer == 10) break;
                if(answer == 150) answer += 100;
            }
        }
        return answer;
    }

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

আমি জানি যে উপরের কোডটি সম্পূর্ণ অপরিহার্য। এটি ভবিষ্যতে এফপিতে স্থানান্তরিত করার পূর্বাভাসের সাথে লেখা হয়নি।


1
মনে রাখবেন যে, সমন্বয় breakএবং return answerএকটি দ্বারা প্রতিস্থাপিত হতে পারে returnলুপ ভিতরে। এফপিতে আপনি ধারাবাহিকতা ব্যবহার করে এই প্রারম্ভিক রিটার্নটি
জর্জিও

1
@ জর্জিও ধারাবাহিকতা এখানে একটি বিশাল ওভারকিল হবে। যাইহোক এটি একটি লুপ, এর পরবর্তী পুনরাবৃত্তিকে কল করার জন্য আপনি একটি লেজ কল করেন, যাতে ভাঙ্গার জন্য আপনি কেবল এটি আর কল করবেন না এবং কেবল উত্তরটি ফিরিয়ে দিন। জন্য নেস্টেড লুপ, বা অন্যান্য জটিল নিয়ন্ত্রণ প্রবাহ, যে আপনি আপনার কোড পুনর্বিন্যাস heaving পরিবর্তে continuations ব্যবহার হতে পারে যেখানে ব্যবহার করা সহজ কৌশল (যা সবসময় সম্ভব হওয়া উচিত, কিন্তু মাত্রাতিরিক্ত জটিল কোড কাঠামো হতে পারে উপরে যা বেশী বা কম would প্রকাশ করা ধারাবাহিকতা; এবং একাধিক বহির্গমন পয়েন্টের জন্য আপনার অবশ্যই তাদের প্রয়োজন হবে)।
নেস 14

8
এই ক্ষেত্রে: takeWhile
জোনাথন

1
@ উইলনেস: আমি কেবল এটি উল্লেখ করতে চেয়েছিলাম কারণ এটি কোনও বিন্দুতে একটি জটিল গণনা ছাড়তে ব্যবহৃত হতে পারে। এটি সম্ভবত ওপি'র দৃ concrete় উদাহরণের জন্য সেরা সমাধান নয়।
জর্জিও

@ জর্জিও আপনি ঠিক বলেছেন, সাধারণভাবে এটি সর্বাধিক বিস্তৃত। আসলে এই প্রশ্নটি খুব বিস্তৃত, আইওয়াইকিউআইএম ​​(অর্থাত্ হৃদস্পন্দনে এসও তে বন্ধ হয়ে যাবে)।
নেস 16

উত্তর:


45

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

doSomeCalc :: [Int] -> Int
doSomeCalc values = foldr1 combine values
  where combine v1 v2 | v1 == 10  = v1
                      | v1 == 150 = v1 + 100 + v2
                      | otherwise = v1 + v2

আপনি হ্যাসেলের সিনট্যাক্সের সাথে পরিচিত না হলে এই রেখাকে লাইন ভেঙে ফেলা, এটি এইরকম কাজ করে:

doSomeCalc :: [Int] -> Int

ফাংশনটির ধরণ সংজ্ঞায়িত করে ইনটগুলির একটি তালিকা গ্রহণ করে এবং একটি একক প্রচ্ছদ প্রদান করে।

doSomeCalc values = foldr1 combine values

ফাংশনটির প্রধান সংস্থা: প্রদত্ত যুক্তি values, foldr1আর্গুমেন্ট combine(যা আমরা নীচে সংজ্ঞায়িত করব) দিয়ে রিটার্ন দেওয়া হয় এবং valuesfoldr1ভাঁজ আদিমের একটি বৈকল্পিক যা তালিকার প্রথম মানতে (তাই 1ফাংশনটির নাম হিসাবে) জমে থাকা সেট দিয়ে শুরু হয় , তারপরে বাম থেকে ডানে ব্যবহারকারী নির্দিষ্ট ফাংশনটি ব্যবহার করে এটি একত্রিত করে (যা সাধারণত ডান ভাঁজ বলা হয় , সুতরাং rফাংশন নাম)। সুতরাং foldr1 f [1,2,3]সমতূল্য f 1 (f 2 3)(অথবা f(1,f(2,3))আরও প্রচলিত সি-মত সিনট্যাক্স মধ্যে)।

  where combine v1 v2 | v1 == 10  = v1

combineস্থানীয় ফাংশন সংজ্ঞায়িত : এটি দুটি আর্গুমেন্ট গ্রহণ করে v1এবং v2। যখন v110 হয়, এটি কেবল ফিরে আসে v1। এই ক্ষেত্রে, ভি 2 কখনই মূল্যায়ন হয় না , সুতরাং লুপটি এখানে থামে।

                      | v1 == 150 = v1 + 100 + v2

বিকল্পভাবে, যখন ভি 1 150 হয়, এতে অতিরিক্ত 100 যোগ করে এবং ভি 2 যুক্ত করে।

                      | otherwise = v1 + v2

এবং, যদি এই শর্তগুলির মধ্যে দুটিও সত্য না হয় তবে কেবল v2-তে V1 যুক্ত করুন।

এখন, এই সমাধানটি হাস্কেলের কাছে কিছুটা নির্দিষ্ট, কারণ সংশ্লেষিত ফাংশনটি তার দ্বিতীয় তর্কটিকে মূল্যায়ন না করে যদি ডান ভাঁজ সমাপ্ত হয় তবে হাস্কেলের অলস মূল্যায়ন কৌশলটি ঘটে। আমি ক্লোজারকে জানি না, তবে আমি বিশ্বাস করি এটি কঠোর মূল্যায়ন ব্যবহার করে, তাই আমি foldএটির প্রাথমিক লাইব্রেরিতে কোনও ফাংশন রাখার প্রত্যাশা করব যাতে প্রাথমিক সমাপ্তির জন্য নির্দিষ্ট সমর্থন অন্তর্ভুক্ত রয়েছে। এটি প্রায়শই বলা হয় foldWhile, foldUntilবা অনুরূপ।

ক্লোজার লাইব্রেরি ডকুমেন্টেশনের এক ঝলক নজরদারি থেকে বোঝা যায় যে এটি নামকরণের ক্ষেত্রে বেশিরভাগ কার্যকরী ভাষা থেকে কিছুটা আলাদা এবং আপনি যা foldখুঁজছেন তা নয় (এটি একটি সমান্তরাল গণনা সক্ষম করার লক্ষ্যে আরও উন্নত পদ্ধতি) তবে reduceআরও সরাসরি সমতুল্য. যদি reducedফাংশনটি আপনার সংযুক্ত ফাংশনটির মধ্যে কল করা হয় তবে প্রাথমিক সমাপ্তি ঘটে । আমি সিনট্যাক্সটি বুঝতে পেরেছি এমন 100% নিশ্চিত নই, তবে আপনি সন্দেহ করছেন যে আপনি যা খুঁজছেন তা এই জাতীয় কিছু:

(reduce 
    (fn [v1 v2]
        (if (= v1 10) 
             (reduced v1)
             (+ v1 v2 (if (= v1 150) 100 0))))
    array)

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


11
নামগুলি v1 v2বিভ্রান্তিকর: v1এটি একটি "অ্যারের থেকে মান", তবে v2এটি জমা হওয়া ফলাফল। এবং আপনার অনুবাদটি ভ্রান্ত, আমি বিশ্বাস করি, সঞ্চিত (বাম থেকে) মানটি 10 এ আঘাত করলে ওপির লুপটি প্রস্থান করে, অ্যারের কোনও উপাদান নয়। 100 দ্বারা বৃদ্ধিশীল সঙ্গে একই ব্যবহার এখানে folds করতে পারেন, ভাঁজ বাঁদিকে প্রকরণ ব্যবহার করেন, প্রথম দিকে প্রস্থান সঙ্গে foldlWhile এখানে
নেস

2
এটা হল মজার কিভাবে সবচেয়ে ভুল উত্তর দঃপূঃ সবচেয়ে upvotes পায় .... এটা ঠিক আছে ভুল করা, আপনি আছেন ভাল কোম্পানীর :) , অত্যধিক। তবে এসও / এসই তে জ্ঞান আবিষ্কারের ব্যবস্থাটি অবশ্যই ভেঙে গেছে।
নেস

1
ক্লোজার কোডটি প্রায় সঠিক, তবে (= v1 150)মানটি ব্যবহারের শর্তটি v2(ওরফে e) সংক্ষেপণের আগে ।
নিকোনিহার

1
Breaking this down line by line in case you're not familiar with Haskell's syntax-- আপনি আমার নায়ক. হাস্কেল আমার কাছে একটি রহস্য।
ক্যাপ্টেন ম্যান

15
@ উইলনেস এটি আপলোড হয়েছে কারণ এটি তাত্ক্ষণিকভাবে উপলব্ধিযোগ্য অনুবাদ এবং ব্যাখ্যা। এটি ভুল যে সত্য তা লজ্জাজনক তবে তুলনামূলকভাবে গুরুত্বহীন কারণ সামান্য ত্রুটি উত্তরটি অন্যথায় সহায়ক যে সত্য তা প্রত্যাখ্যান করে না। তবে অবশ্যই এটি সংশোধন করা উচিত।
কনরাড রুডলফ

33

আপনি এটিকে সহজেই পুনরাবৃত্তিতে রূপান্তর করতে পারেন। এবং এটিতে সুন্দর লেজ-অনুকূলিত পুনরাবৃত্ত কল রয়েছে।

সিউডোকোড:

public int doSomeCalc(int[] array)
{
    return doSomeCalcInner(array, 0);
}

public int doSomeCalcInner(int[] array, int answer)
{
    if (array is empty) return answer;

    // not sure how to efficiently implement head/tails array split in clojure
    var head = array[0] // first element of array
    var tail = array[1..] // remainder of array

    answer += head;
    if (answer == 10) return answer;
    if (answer == 150) answer += 100;

    return doSomeCalcInner(tail, answer);
}

14
হাঁ। লুপের কার্যক্ষম সমতুল্য হ'ল লেজ-পুনরাবৃত্তি, এবং শর্তসাপেক্ষিকের সমতুল্য ক্রিয়াকলাপটি এখনও শর্তসাপেক্ষ।
জার্গ ডব্লু মিত্তাগ

4
@ জার্গডাব্লু মিটাগ আমি বরং বলব লেজ পুনরাবৃত্তিটি কার্যকারিতা সমতুল্য GOTO। (বেশ খারাপ হিসাবে না, তবে এখনও বেশ বিশ্রী।) জুলস যেমন বলে একটি লুপের সমতুল্য একটি উপযুক্ত ভাঁজ।
বামপাশে

6
@ বাম দিক সম্পর্কে আমি আসলে একমত নই আমি বলব লেজ পুনরাবৃত্তি একটি গোটোর চেয়ে আরও সীমাবদ্ধ, নিজের এবং কেবল লেজ অবস্থানে ঝাঁপিয়ে পড়ার প্রয়োজনীয়তার প্রয়োজনে। এটি মূলত একটি লুপিং নির্মাণের সমতুল্য। আমি বলতে চাই যে সাধারণভাবে পুনরাবৃত্তি সমান GOTO। যে কোনও ক্ষেত্রে, আপনি পুচ্ছ পুনরাবৃত্তিটি সংকলন করার সময় এটি বেশিরভাগই কেবল while (true)ফাংশন বডি সহ একটি লুপে সিদ্ধ হয় যেখানে প্রারম্ভিক রিটার্ন কেবল একটি breakবিবৃতি। একটি ভাঁজ, যদিও আপনি এটি লুপ হওয়ার বিষয়ে সঠিক, আসলে একটি সাধারণ লুপিং কনস্ট্রাক্টের চেয়ে বেশি সীমাবদ্ধ; এটি প্রতিটি লুপের মতো আরও
J_mie6

1
@ J_mie6 যে কারণে আমি লেজ পুনরাবৃত্তিটিকে বেশি হিসাবে বিবেচনা GOTOকরি তা হ'ল পুনরাবৃত্ত কলকে কী অবস্থায় যুক্ত করা হয়েছে তা কোনটি যুক্তি সহকারে আপনার পুঙ্খানুপুঙ্খভাবে বুককিপিং করা উচিত, এটি নিশ্চিত করতে যে এটি আসলে উদ্দেশ্য হিসাবে আচরণ করে। শালীনভাবে লিখিত আবশ্যকীয় লুপগুলিতে এটি একই ডিগ্রির জন্য প্রয়োজনীয় নয় (যেখানে এটি স্পষ্টভাবে স্পষ্ট হয় যে রাষ্ট্রীয় পরিবর্তনশীলগুলি কী এবং প্রতিটি পুনরাবৃত্তির ক্ষেত্রে তারা কীভাবে পরিবর্তিত হয়), বা নির্দোষ পুনরাবৃত্তিতে (যেখানে সাধারণত আর্গুমেন্টের সাহায্যে খুব বেশি কিছু করা হয় না, এবং পরিবর্তে) ফলাফলটি বেশ স্বজ্ঞাত উপায়ে একত্রিত হয়)। ...
12:59 এ বাম দিকের বাইরে

1
... ভাঁজগুলির ক্ষেত্রে: আপনি ঠিক বলেছেন, একটি traditionalতিহ্যবাহী ভাঁজ (ক্যাটামোর্ফিজম) একটি খুব নির্দিষ্ট ধরণের লুপ তবে এই পুনরাবৃত্তি স্কিমগুলি সাধারণীকরণ করা যেতে পারে (আনা / এপিও / হাইলোমর্ফিজম); সম্মিলিতভাবে এগুলি হ'ল অপরিহার্য লুপগুলির যথাযথ প্রতিস্থাপন IM
12:59 এ বাম দিকের বাইরে

13

আমি সত্যিই জুলেসের উত্তর পছন্দ করি , তবে আমি অতিরিক্তভাবে অলস ফাংশনাল প্রোগ্রামিং সম্পর্কে লোকজনকে মিস করা এমন কিছু বিষয়ও উল্লেখ করতে চেয়েছিলাম, যা হ'ল সবকিছু "লুপের ভিতরে" থাকতে হয় না। উদাহরণ স্বরূপ:

baseSums = scanl (+) 0

offsets = scanl (\offset sum -> if sum == 150 then offset + 100 else offset) 0

zipWithOffsets xs = zipWith (+) xs (offsets xs)

stopAt10 xs = if 10 `elem` xs then 10 else last xs

result = stopAt10 . zipWithOffsets . baseSums

result [1..]         -- 10
result [11..1000000] -- 500000499945

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


যুক্তি এখানে ছড়িয়ে ছিটিয়ে আছে। এই কোড বজায় রাখা সহজ হবে না। stopAt10হয় না একটি ভাল ভোক্তা। আপনার উত্তর হয় আপনি যে এটি সঠিকভাবে মৌলিক প্রযোজক isolates cite বেশী ভালো scanl (+) 0মূল্যবোধের। তাদের গ্রাহকরা নিয়ন্ত্রণ যুক্তিকে সরাসরি অন্তর্ভুক্ত করা উচিত যদিও স্পষ্টভাবে কেবল দুটি spanএস এবং এ দিয়ে কার্যকর করা হয়েছে last। এটি খুব কাছে থেকে মূল কোড কাঠামো এবং যুক্তিগুলিও অনুসরণ করবে এবং এটি বজায় রাখা সহজ হবে।
নেস 16

6

সর্বাধিক তালিকা প্রক্রিয়াকরণ উদাহরণ তোমার মত ব্যবহার ফাংশন দেখতে হবে map, filter, sumইত্যাদি যা সামগ্রিকভাবে তালিকায় কাজ করে। তবে আপনার ক্ষেত্রে আপনার শর্তসাপেক্ষে প্রারম্ভিক প্রস্থান রয়েছে - একটি অস্বাভাবিক প্যাটার্ন যা সাধারণ তালিকা ক্রিয়াকলাপ দ্বারা সমর্থিত নয়। সুতরাং আপনার একটি বিমূর্ত স্তর নীচে নেমে যাওয়া এবং পুনরাবৃত্তি ব্যবহার করা দরকার - যা আবশ্যক উদাহরণটি কীভাবে দেখায় তারও কাছাকাছি।

এটি ক্লোজুরে এর পরিবর্তে সরাসরি (সম্ভবত মূio় নয়) অনুবাদ:

(defn doSomeCalc 
  ([lst] (doSomeCalc lst 0))
  ([lst sum]
    (if (empty? lst) sum
        (if (= sum 10) sum
            (let [sum (+ sum (first lst))]
                 [sum (if (= sum 150) (+ sum 100) sum)]
               (recur (rest lst) sum))))))) 

সম্পাদনা: যে জুলে বিন্দু reduceClojure মধ্যে কি গোড়ার দিকে প্রস্থান সমর্থন করি। এটি ব্যবহার করা আরও মার্জিত:

(defn doSomeCalc [lst]  
  (reduce (fn [sum val]
    (if (= sum 10) (reduced sum)
        (let [sum (+ sum val)]
             [sum (if (= sum 150) (+ sum 100) sum)]
           sum))
   lst)))

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


আমি আমার উত্তরে সবেমাত্র সম্পাদনাটি দেখুন: ক্লোজারের reduceঅপারেশনটি প্রারম্ভিক প্রস্থানকে সমর্থন করে।
জুলাই

@ জুলস: শীতল - এটি সম্ভবত আরও একটি বুদ্ধিমান সমাধান।
জ্যাকবিবি

ভুল - বা takeWhile'সাধারণ অপারেশন' নয়?
জোনাথন

@ জাস্টকাস্ট - যদিও takeWhileএটি একটি সাধারণ অপারেশন, তবে এটি বিশেষত কার্যকর হয় না, কারণ থামার বিষয়ে সিদ্ধান্ত নেওয়ার আগে আপনার পরিবর্তনের ফলাফলগুলি আপনার প্রয়োজন। অলস ভাষায় এটির কোনও গুরুত্ব নেই: আপনি স্ক্যানের ফলাফলগুলি ব্যবহার করতে পারেন scanএবং takeWhileদেখুন (কার্ল বিলেফেল্টের উত্তর দেখুন যা এটি ব্যবহার না করে takeWhileসহজেই এটি আবার লিখতে পারে), তবে ক্লোজারের মতো কঠোর ভাষার জন্য এটি হবে মানে পুরো তালিকাটি প্রক্রিয়া করা এবং তারপরে ফলাফলগুলি বাতিল করা। জেনারেটরের ফাংশনগুলি এটি সমাধান করতে পারে তবে আমি বিশ্বাস করি ক্লোজার তাদের সমর্থন করে।
জুলাই 10

ক্লোজারের জুলজগুলি take-whileঅলস ক্রম তৈরি করে (ডক্স অনুসারে)। এটি মোকাবেলার আরেকটি উপায় হ'ল ট্রান্সডুসারদের সাথে (সম্ভবত ) সহ।
নেস

4

অন্যান্য উত্তরের দ্বারা নির্দেশিত হিসাবে, ক্লোজারের reducedতাড়াতাড়ি হ্রাস বন্ধ করার জন্য রয়েছে :

(defn some-calc [coll]
  (reduce (fn [answer e]
            (let [answer (+ answer e)]
               (case answer
                 10  (reduced answer)
                 150 (+ answer 100)
                 answer)))
          0 coll))

এটি আপনার নির্দিষ্ট পরিস্থিতির জন্য সেরা সমাধান। এছাড়াও আপনি মিশ্রন থেকে মাইলেজ কত অনেক পেতে পারেন reducedসঙ্গে transduce, যা দেয় আপনার কাছ থেকে transducers ব্যবহার map, filterইত্যাদি তবে এটি আপনার সাধারণ প্রশ্নের সম্পূর্ণ উত্তর থেকে অনেক দূরে।

এস্কেপ ধারাবাহিকতা বিরতি এবং রিটার্নের বিবৃতিগুলির একটি সাধারণ সংস্করণ। এগুলি কিছু স্কিম ( call-with-escape-continuation), কমন লিস্প ( block+ return, catch+ throw) এমনকি সি ( setjmp+ longjmp) এ সরাসরি প্রয়োগ করা হয় । স্ট্যান্ডার্ড স্কিম বা হাস্কেল এবং স্কালায় ক্রমাগত মনড হিসাবে পাওয়া যায় এমন আরও সাধারণ সীমিত বা অনির্দিষ্টকৃত ধারাবাহিকতাগুলিও পালানোর ধারাবাহিকতা হিসাবে ব্যবহার করা যেতে পারে।

উদাহরণস্বরূপ, র‌্যাকেটে আপনি এটি ব্যবহার করতে পারেন let/ec:

(define (some-calc ls)
  (let/ec break ; let break be an escape continuation
    (foldl (lambda (answer e)
             (let ([answer (+ answer e)])
               (case answer
                 [(10)  (break answer)] ; return answer immediately
                 [(150) (+ answer 100)]
                 [else  answer])))
           0 ls)))

অন্যান্য অনেক ভাষায়ও ব্যতিক্রম হ্যান্ডলিংয়ের আকারে অব্যাহত থাকার মতো নির্মাণ রয়েছে। হাসকেলে আপনি এর সাথে বিভিন্ন ত্রুটি মনডগুলির মধ্যে একটি ব্যবহার করতে পারেন foldM। যেহেতু তারা প্রাথমিকভাবে প্রাথমিক রিটার্নগুলির জন্য ব্যতিক্রম বা ত্রুটি স্নাদ ব্যবহার করে কনস্ট্রাক্টগুলি পরিচালনা করতে ত্রুটিযুক্ত হয় সাধারণত সাংস্কৃতিকভাবে অগ্রহণযোগ্য এবং সম্ভবত বেশ ধীর হয়।

আপনি হাই-অর্ডার ফাংশন থেকে টেইল কলগুলিতেও নামতে পারেন।

লুপগুলি ব্যবহার করার সময়, আপনি লুপের বডিটির শেষে পৌঁছালে স্বয়ংক্রিয়ভাবে পরবর্তী পুনরাবৃত্তিটি প্রবেশ করুন enter আপনি পরবর্তী পুনরাবৃত্তিটি প্রারম্ভিক শুরুতে প্রবেশ করতে পারেন continueবা break(বা return) দিয়ে লুপটি প্রস্থান করতে পারেন । টেইল কলগুলি ব্যবহার করার সময় (বা ক্লুজুরের loopকনস্ট্রাক্ট যা পুচ্ছ পুনরাবৃত্তি অনুকরণ করে) আপনার পরবর্তী পুনরাবৃত্তিতে প্রবেশের জন্য সর্বদা একটি স্পষ্ট কল করতে হবে। লুপিং বন্ধ করতে আপনি কেবল পুনরাবৃত্তি কল করবেন না তবে সরাসরি মূল্য দিন:

(defn some-calc [coll]
  (loop [answer 0, [e es :as coll] coll]
    (if (empty? coll)
      answer
      (let [answer (+ answer e)]
        (case answer
          10 answer
          150 (recur (+ answer 100) es)
          (recur answer es))))))

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

@ জুলস আমি মনে করি বাম দিকে ফিরে আসা ভাঁজ পুরো তালিকা (বা অন্যান্য ক্রম) এ যাওয়া থেকে আটকাবে না। যদিও হাস্কেল স্ট্যান্ডার্ড লাইব্রেরি ইন্টার্নালগুলির সাথে ঘনিষ্ঠভাবে পরিচিত নয়।
নীলারন

2

জটিলতম অংশটি লুপ। আমাদের এটি দিয়ে শুরু করা যাক। একটি লুপ সাধারণত একক ফাংশন দিয়ে পুনরাবৃত্তি প্রকাশ করে ক্রিয়ামূলক শৈলীতে রূপান্তরিত হয়। একটি পুনরাবৃত্তি হ'ল লুপ ভেরিয়েবলের রূপান্তর।

এখানে একটি সাধারণ লুপের কার্যকরী বাস্তবায়ন:

loop : v -> (v -> v) -> (v -> Bool) -> v
loop init iter cond_to_cont = 
    if cond_to_cont init 
        then loop (iter init) iter cond
        else init

এটি লাগে (লুপ ভেরিয়েবলের একটি প্রাথমিক মান, ফাংশন যা একটি একক পুনরাবৃত্তি [লুপ ভেরিয়েবলের উপরে প্রকাশ করে)) (লুপটি চালিয়ে যাওয়ার শর্ত)।

আপনার উদাহরণটি অ্যারেতে একটি লুপ ব্যবহার করে যা ভেঙে যায়। আপনার অপরিহার্য ভাষায় এই ক্ষমতাটি ভাষাতেই বেকড। কার্যকরী প্রোগ্রামিংয়ে এ জাতীয় ক্ষমতা সাধারণত গ্রন্থাগার পর্যায়ে প্রয়োগ করা হয়। এখানে একটি সম্ভাব্য বাস্তবায়ন

module Array (foldlc) where

foldlc : v -> (v -> e -> v) -> (v -> Bool) -> Array e -> v
foldlc init iter cond_to_cont arr = 
    loop 
        (init, 0)
        (λ (val, next_pos) -> (iter val (at next_pos arr), next_pos + 1))
        (λ (val, next_pos) -> and (cond_to_cont val) (next_pos < size arr))

এটা :

আমি একটি ((ভাল, নেক্সট_পোস)) জোড়া ব্যবহার করব যা লুপ ভেরিয়েবলের বাইরে দৃশ্যমান এবং অ্যারেতে অবস্থান রাখবে যা এই ফাংশনটি আড়াল করে।

সাধারণ লুপের তুলনায় পুনরাবৃত্তি ফাংশনটি কিছুটা জটিল, এই সংস্করণটি অ্যারের বর্তমান উপাদানটি ব্যবহার সম্ভব করে। [এটি তরকারিযুক্ত আকারে রয়েছে।]

এই জাতীয় ফাংশনগুলির নাম সাধারণত "ভাঁজ" করা হয়।

অ্যারের উপাদানগুলির সংचयটি বাম-সহযোগী পদ্ধতিতে সম্পন্ন হয়েছে তা বোঝাতে আমি নামে একটি "এল" রেখেছি; নিম্ন থেকে উচ্চ সূচক পর্যন্ত কোনও অ্যারে পুনরুক্ত করার জন্য অপরিহার্য প্রোগ্রামিং ভাষার অভ্যাসের অনুকরণ করতে।

আমি নামের মধ্যে একটি "সি" রেখেছি যে ফোল্ডের এই সংস্করণটি এমন একটি শর্ত নেয় যা কখন এবং কখন লুপটি বন্ধ করা উচিত তা নিয়ন্ত্রণ করে।

অবশ্যই এই ধরনের ইউটিলিটি ফাংশনগুলি সম্ভবত কার্যকরী প্রোগ্রামিং ভাষার সাথে ব্যবহৃত বেস লাইব্রেরিতে সহজলভ্য হতে পারে। আমি এখানে তাদের প্রদর্শনের জন্য লিখেছি।

অত্যাবশ্যকীয় ক্ষেত্রে আমাদের কাছে সেই সমস্ত সরঞ্জাম রয়েছে যা ভাষায় রয়েছে, আমরা আপনার উদাহরণের নির্দিষ্ট কার্যকারিতাটি বাস্তবায়িত করতে পারি to

আপনার লুপের পরিবর্তনশীলটি একটি জুড়ি ('উত্তর', একটি বুলিয়ান যা চালিয়ে যাওয়া উচিত কিনা তা এনকোড করে)।

iter : (Int, Bool) -> Int -> (Int, Bool)
iter (answer, cont) collection_element = 
  let new_answer = answer + collection_element
  in case new_answer of
    10 -> (new_answer, false)
    150 -> (new_answer + 100, true)
    _ -> (new_answer, true)

মনে রাখবেন যে আমি একটি নতুন "ভেরিয়েবল" 'new_answer' ব্যবহার করেছি। এটি কারণ ক্রিয়ামূলক প্রোগ্রামিংয়ে আমি ইতিমধ্যে আরম্ভকৃত "ভেরিয়েবল" এর মান পরিবর্তন করতে পারি না। আমি পারফরম্যান্স নিয়ে চিন্তিত নই, সংকলকটি যদি আরও দক্ষ বলে মনে করে তবে লাইফ-টাইম বিশ্লেষণের মাধ্যমে 'নতুন_উত্তরকারী' এর জন্য 'উত্তর' এর স্মৃতি পুনরায় ব্যবহার করতে পারে।

আমাদের লুপ ফাংশনটিতে এটি ইতিপূর্বে বিকাশ করা হয়েছে:

doSomeCalc :: Array Int -> Int
doSomeCalc arr = fst (Array.foldlc (0, true) iter snd arr)

"অ্যারে" এখানে মডিউলটির নাম যা রফতানি ফোল্ডেলসিটি হয়।

"মুষ্টি", "সেকেন্ড" ফাংশনগুলির জন্য দাঁড়ায় যা তার জোড়া প্যারামিটারের প্রথম, দ্বিতীয় উপাদানটি দেয় returns

fst : (x, y) -> x
snd : (x, y) -> y

এই ক্ষেত্রে "বিন্দু মুক্ত" শৈলী doSomeCalc বাস্তবায়নের পাঠযোগ্যতা বৃদ্ধি করে:

doSomeCalc = Array.foldlc (0, true) iter snd >>> fst

(>>>) হল ফাংশন কম্পোজিশন: (>>>) : (a -> b) -> (b -> c) -> (a -> c)

এটি উপরের মতোই, সংজ্ঞায়িত সমীকরণের উভয় দিক থেকে কেবল "আরর" প্যারামিটারটি বাকী রয়েছে।

একটি শেষ জিনিস: কেস (অ্যারে == নাল) জন্য পরীক্ষা করা। উন্নততর নকশাকৃত প্রোগ্রামিং ভাষাগুলিতে, তবে কিছু প্রাথমিক শৃঙ্খলাযুক্ত খারাপভাবে নকশাকৃত ভাষাগুলিতেও এটি অ-অস্তিত্ব প্রকাশ করার জন্য একটি optionচ্ছিক প্রকার ব্যবহার করে । এটি কার্যকরী প্রোগ্রামিংয়ের সাথে খুব বেশি সম্পর্ক রাখে না, যা সম্পর্কে প্রশ্নটি শেষ পর্যন্ত হয়, সুতরাং আমি এটি নিয়ে কাজ করি না।


0

প্রথমে লুপটি কিছুটা আবার লিখুন, যেমন লুপটির প্রতিটি পুনরাবৃত্তি হয় তাড়াতাড়ি প্রস্থান করে, বা answerঠিক একবার পরিবর্তিত হয় :

    public int doSomeCalc(int[] array)
    {
        int answer = 0;
        if(array!=null)
        {
            for(int e: array)
            {
                if(answer + e == 10) return answer + e;
                else if(answer + e == 150) answer = answer + e + 100;
                else answer = answer + e;
            }
        }
        return answer;
    }

এটি স্পষ্ট হওয়া উচিত যে এই সংস্করণটির আচরণটি পূর্বের মতো ঠিক একই রকম, তবে এখন এটি পুনরাবৃত্ত শৈলীতে রূপান্তর করা আরও অনেক সোজা। এখানে সরাসরি হাস্কেল অনুবাদ রয়েছে:

doSomeCalc :: [Int] -> Int
doSomeCalc = recurse 0
  where recurse :: Int -> [Int] -> Int
        recurse answer [] = answer
        recurse answer (e:array)
          | answer + e == 10 = answer + e
          | answer + e == 150 = recurse (answer + e + 100) array
          | otherwise = recurse (answer + e) array

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

import Control.Monad (foldM)

doSomeCalc :: [Int] -> Int
doSomeCalc = either id id . foldM go 0
  where go :: Int -> Int -> Either Int Int
        go answer e
          | answer + e == 10 = Left (answer + e)
          | answer + e == 150 = Right (answer + e + 100)
          | otherwise = Right (answer + e)

এই প্রসঙ্গে, Leftপ্রারম্ভিক তার মান সহ প্রস্থান করে এবং তার মান Rightসহ পুনরাবৃত্তি চালিয়ে যায়।


এটি এখন আরও কিছুটা সরল করা যায়, এর মতো:

import Control.Monad (foldM)

doSomeCalc :: [Int] -> Int
doSomeCalc = either id id . foldM go 0
  where go :: Int -> Int -> Either Int Int
        go answer e
          | answer' == 10 = Left 10
          | answer' == 150 = Right 250
          | otherwise = Right answer'
          where answer' = answer + e

এটি চূড়ান্ত হাস্কেল কোড হিসাবে ভাল, তবে এটি এখন আসল জাভাতে কীভাবে মানচিত হয় তা কিছুটা কম স্পষ্ট।

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