আমি নিজেকে পুনরাবৃত্তি না করে এই অ্যালগরিদমকে কীভাবে আলস্য করে তুলব?


9

( এই প্রশ্নের আমার উত্তর দ্বারা অনুপ্রাণিত ।)

এই কোডটি বিবেচনা করুন (প্রদত্ত ইনপুটের চেয়ে কম বা সমান যে বৃহত্তম উপাদান এটি সন্ধান করা উচিত):

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Nothing where
  precise :: Maybe (Integer, v) -> TreeMap v -> Maybe (Integer, v)
  precise closestSoFar Leaf = closestSoFar
  precise closestSoFar (Node k v l r) = case i `compare` k of
    LT -> precise closestSoFar l
    EQ -> Just (k, v)
    GT -> precise (Just (k, v)) r

এটি খুব অলস নয়। একবার GTকেস প্রবেশ করা গেলে , আমরা নিশ্চিতভাবে জানি যে চূড়ান্ত রিটার্ন মান Justপরিবর্তে কিছু হবে Nothing, তবে Justএখনও শেষ পর্যন্ত পাওয়া যায় না। আমি এই অলসটি তৈরি করতে চাই যাতে কেস প্রবেশের Justসাথে সাথেই এটি উপলব্ধ হয় GT। এর জন্য আমার পরীক্ষার কেসটি হ'ল আমি বোতলের চেয়ে Data.Maybe.isJust $ closestLess 5 (Node 3 () Leaf undefined)মূল্যায়ন করতে চাই True। এটি করার জন্য আমি ভাবতে পারি এমন এক উপায়:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess _ Leaf = Nothing
closestLess i (Node k v l r) = case i `compare` k of
  LT -> closestLess i l
  EQ -> Just (k, v)
  GT -> Just (precise (k, v) r)
  where
    precise :: (Integer, v) -> TreeMap v -> (Integer, v)
    precise closestSoFar Leaf = closestSoFar
    precise closestSoFar (Node k v l r) = case i `compare` k of
      LT -> precise closestSoFar l
      EQ -> (k, v)
      GT -> precise (k, v) r

যাইহোক, আমি এখন নিজেকে পুনরাবৃত্তি করছি: মূল যুক্তিটি এখন এবং উভয়ই closestLessমধ্যে precise। আমি কীভাবে এটি লিখতে পারি যাতে এটি অলস কিন্তু নিজের কাছে পুনরাবৃত্তি না করে?

উত্তর:


4

সুস্পষ্ট মোড়ক ব্যবহারের পরিবর্তে আপনি টাইপ সিস্টেমটি উপকার করতে পারেন। মনে রাখবেন যে সংস্করণটি আপনার প্রথম কোড স্নিপেটের জন্য preciseব্যবহার করে Maybe:

precise :: Maybe (Integer, v) -> TreeMap v -> Maybe (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
  LT -> precise closestSoFar l
  EQ -> Just (k, v)
  GT -> precise (Just (k, v)) r

আপনার দ্বিতীয় কোড স্নিপেট preciseব্যতীত সংস্করণ হিসাবে প্রায় ঠিক একই অ্যালগরিদম Maybeযা Identityফান্টারে লেখা যেতে পারে :

precise :: Identity (Integer, v) -> TreeMap v -> Identity (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
  LT -> precise closestSoFar l
  EQ -> Identity (k, v)
  GT -> precise (Identity (k, v)) r

এগুলিকে এক সংস্করণ পলিমার্ফিকে একীকরণ করা যেতে পারে Applicative:

precise :: (Applicative f) => f (Integer, v) -> TreeMap v -> f (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
  LT -> precise closestSoFar l
  EQ -> pure (k, v)
  GT -> precise (pure (k, v)) r

নিজেই, এটি খুব বেশি অর্জন করে না, তবে আমরা যদি জানি যে GTশাখাটি সর্বদা একটি মান ফিরিয়ে দেবে, আমরা Identityপ্রারম্ভিক ফান্টর নির্বিশেষে, এটি ফান্টারে চালাতে বাধ্য করতে পারি । অর্থাৎ আমরা শুরু করতে পারেন Maybefunctor কিন্তু মধ্যে recurse Identityমধ্যে functor GTশাখা:

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Nothing
  where
    precise :: (Applicative t) => t (Integer, v) -> TreeMap v -> t (Integer, v)
    precise closestSoFar Leaf = closestSoFar
    precise closestSoFar (Node k v l r) = case i `compare` k of
      LT -> precise closestSoFar l
      EQ -> pure (k, v)
      GT -> pure . runIdentity $ precise (Identity (k, v)) r

এটি আপনার পরীক্ষার ক্ষেত্রে ভাল কাজ করে:

> isJust $ closestLess 5 (Node 3 () Leaf undefined)
True

এবং বহুবর্ষীয় পুনরাবৃত্তির একটি দুর্দান্ত উদাহরণ।

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

closestLess
  = \ @ v i eta ->
      letrec {
        $sprecise
        $sprecise
          = \ @ v1 closestSoFar ds ->
              case ds of {
                Leaf -> closestSoFar;
                Node k v2 l r ->
                  case compareInteger i k of {
                    LT -> $sprecise closestSoFar l;
                    EQ -> (k, v2) `cast` <Co:5>;
                    GT -> $sprecise ((k, v2) `cast` <Co:5>) r
                  }
              }; } in
      letrec {
        $sprecise1
        $sprecise1
          = \ @ v1 closestSoFar ds ->
              case ds of {
                Leaf -> closestSoFar;
                Node k v2 l r ->
                  case compareInteger i k of {
                    LT -> $sprecise1 closestSoFar l;
                    EQ -> Just (k, v2);
                    GT -> Just (($sprecise ((k, v2) `cast` <Co:5>) r) `cast` <Co:4>)
                  }
              }; } in
      $sprecise1 Nothing eta

2
এটি একটি দুর্দান্ত সমাধান
লুচি

3

আমার অলস বাস্তবায়ন থেকে শুরু করে, আমি প্রথমে একটি যুক্তি হিসাবে preciseগ্রহণ করার জন্য রিফ্যাক্টর করেছিলাম Justএবং সেই অনুসারে এর ধরণটিকে সাধারণীকরণ করেছি:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Just Nothing where
  precise :: ((Integer, v) -> t) -> t -> TreeMap v -> t
  precise _ closestSoFar Leaf = closestSoFar
  precise wrap closestSoFar (Node k v l r) = case i `compare` k of
    LT -> precise wrap closestSoFar l
    EQ -> wrap (k, v)
    GT -> precise wrap (wrap (k, v)) r

তারপর, আমি কি করতে এটি পরিবর্তিত wrapতাড়াতাড়ি এবং নিজেই কল idমধ্যে GTমামলা:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Just Nothing where
  precise :: ((Integer, v) -> t) -> t -> TreeMap v -> t
  precise _ closestSoFar Leaf = closestSoFar
  precise wrap closestSoFar (Node k v l r) = case i `compare` k of
    LT -> precise wrap closestSoFar l
    EQ -> wrap (k, v)
    GT -> wrap (precise id (k, v) r)

এটি এখনও যুক্ত হিসাবে ঠিক কাজ করে, যুক্ত অলসতার সুবিধা ব্যতীত।


1
idএর মধ্যে কি সবগুলি মধ্যবর্তী Justএবং চূড়ান্তভাবে (k,v)সংকলক দ্বারা মুছে ফেলা হচ্ছে? সম্ভবত না, ফাংশনগুলি অস্বচ্ছ বলে মনে করা হয় এবং আপনি সমস্ত সংকলক জানেন না তার first (1+)পরিবর্তে আপনি (টাইপ-ফিজাব্যালি) ব্যবহার করতে পারেন id। তবে এটি একটি কমপ্যাক্ট কোডটি তৈরি করে ... অবশ্যই, আমার কোডটি এখানে আপনার সরলকরণের অতিরিক্ত সরলীকরণ (গুলিগুলির নির্মূলকরণ) এর আপনার সমাধান এবং স্পেসিফিকেশন id। এছাড়াও আরও আকর্ষণীয় যে আরও সাধারণ ধরণ কীভাবে প্রতিবন্ধকতা হিসাবে কাজ করে, জড়িত মানের মধ্যে একটি সম্পর্ক (যদিও যথেষ্ট টাইট নয়, first (1+)হিসাবে অনুমোদিত হওয়ার সাথে wrap)।
নেস

1
(নিয়মিত) আপনার বহুবিক preciseদুটি প্রকারে ব্যবহৃত হয়, আরও ভার্বোস বৈকল্পিকের জন্য ব্যবহৃত দুটি বিশেষ ফাংশনের সাথে সরাসরি মিলিত। সেখানে ভাল ইন্টারপ্লে। এছাড়াও, আমি এই সিপিএস কল করব না, wrapধারাবাহিকতা হিসাবে ব্যবহৃত হয় না, এটি "অভ্যন্তরে" নির্মিত হয় না, এটি স্ট্যাক করা হয় - পুনরাবৃত্তি দ্বারা - বাইরের দিকে। হতে পারে যদি এটি ধারাবাহিকতা হিসাবে ব্যবহার করা হত আপনি সেই বহিরাগতদের id... বিটিডব্লিউ থেকে মুক্তি পেতে পারেন তবে আমরা আবারও দেখতে পাচ্ছি যে কার্যকরী আর্গুমেন্টের সেই পুরানো প্যাটার্নটি করণীয় ( Justবা id) দুটি কোর্সের মধ্যে স্যুইচিংয়ের জন্য নির্দেশক হিসাবে ব্যবহৃত হয়েছিল ।
নেস

3

আমি মনে করি আপনি যে সিপিএস সংস্করণটি নিজের সাথে জবাব দিয়েছেন সেটি সেরা তবে সম্পূর্ণতার জন্য এখানে আরও কয়েকটি ধারণা রয়েছে। (সম্পাদনা: বুহরের উত্তর এখন সবচেয়ে পারফরম্যান্ট)

প্রথম ধারণাটি " closestSoFar" সঞ্চয়ের হাত থেকে মুক্তি পাওয়া উচিত এবং তার পরিবর্তে GTমামলার পক্ষে যুক্তির চেয়ে সবচেয়ে সঠিক মানকে বাছাই করার সমস্ত যুক্তি হ্যান্ডেল করা উচিত। এই ফর্মটিতে, GTকেস সরাসরি একটি ফিরে আসতে পারে Just:

closestLess1 :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess1 _ Leaf = Nothing
closestLess1 i (Node k v l r) =
  case i `compare` k of
    LT -> closestLess1 i l
    EQ -> Just (k, v)
    GT -> Just (fromMaybe (k, v) (closestLess1 i r))

এটি সহজ, তবে যখন আপনি প্রচুর GTক্ষেত্রে আঘাত করেন তখন স্ট্যাকের জন্য আরও কিছুটা জায়গা লাগে । প্রযুক্তিগতভাবে আপনি এটি ব্যবহারকারীর fromMaybeআকারেও ব্যবহার করতে পারেন (অর্থাত্ fromJustলুকির উত্তরের অন্তর্ভুক্ত প্রতিস্থাপন ), তবে এটি একটি অপ্রয়োজনীয়, অ্যাক্সেসযোগ্য শাখা হবে।

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

data SBool (b :: Bool) where
  STrue :: SBool 'True
  SFalse :: SBool 'False

type family MaybeUnless (b :: Bool) a where
  MaybeUnless 'True a = a
  MaybeUnless 'False a = Maybe a

ret :: SBool b -> a -> MaybeUnless b a
ret SFalse = Just
ret STrue = id

closestLess2 :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess2 i = precise SFalse Nothing where
  precise :: SBool b -> MaybeUnless b (Integer, v) -> TreeMap v -> MaybeUnless b (Integer, v)
  precise _ closestSoFar Leaf = closestSoFar
  precise b closestSoFar (Node k v l r) = case i `compare` k of
    LT -> precise b closestSoFar l
    EQ -> ret b (k, v)
    GT -> ret b (precise STrue (k, v) r)

আমার উত্তরটি সিপিএস হিসাবে ভাবেনি যতক্ষণ না আপনি এটি নির্দেশ করেছেন। আমি একজন কর্মী-র‌্যাপার ট্রান্সফর্মের কাছাকাছি কিছু সম্পর্কে ভাবছিলাম। আমার ধারণা রেমন্ড চেন আবার আঘাত করেছে!
জোসেফ সিবিল-পুনরায় ইনস্টল করুন মনিকা

2

কেমন

GT -> let Just v = precise (Just (k,v) r) in Just v

?


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

সুতরাং আপনি কিছু সন্দেহের সাথে "আমরা নিশ্চিত জানি" বলেছিলেন। সম্ভবত এটি স্বাস্থ্যকর।
লুচি

আমরা নিশ্চিতভাবে জানি, প্রদত্ত যে আমার প্রশ্নে আমার দ্বিতীয় কোড ব্লক সর্বদা ফিরে আসে Justতবে মোট is আমি জানি যে লিখিত হিসাবে আপনার সমাধান আসলে মোট, কিন্তু এটি আপাতদৃষ্টিতে-নিরাপদ পরিবর্তনের ফলে এটি বোমা ফেলার ফল হতে পারে তা ভঙ্গুর।
জোসেফ সিবিল-পুনরায় ইনস্টল করুন মনিকা

এটি প্রোগ্রামটি কিছুটা কমিয়ে দেবে, কারণ জিএইচসি প্রমাণ করতে পারে না যে এটি সর্বদা থাকবে Just, সুতরাং এটি Nothingপ্রতিবারের দ্বারা পুনরাবৃত্তি হওয়ার পরে এটি একবার নয় তা নিশ্চিত করার জন্য এটি একটি পরীক্ষা যুক্ত করবে ।
জোসেফ সিবিল-পুনরায় ইনস্টল করুন মনিকা

1

শুধু আমরা সবসময় জানো Just, পরে তার প্রথম আবিষ্কার, আমরা সবসময় জানতে Nothing পর্যন্ত তারপর। এটি আসলে দুটি ভিন্ন "লজিক"।

সুতরাং, আমরা প্রথমে বামে চলেছি, সুতরাং এটি স্পষ্ট করে তুলুন:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) 
                 deriving (Show, Read, Eq, Ord)

closestLess :: Integer 
            -> TreeMap v 
            -> Maybe (Integer, v)
closestLess i = goLeft 
  where
  goLeft :: TreeMap v -> Maybe (Integer, v)
  goLeft n@(Node k v l _) = case i `compare` k of
          LT -> goLeft l
          _  -> Just (precise (k, v) n)
  goLeft Leaf = Nothing

  -- no more maybe if we're here
  precise :: (Integer, v) -> TreeMap v -> (Integer, v)
  precise closestSoFar Leaf           = closestSoFar
  precise closestSoFar (Node k v l r) = case i `compare` k of
        LT -> precise closestSoFar l
        EQ -> (k, v)
        GT -> precise (k, v) r

দামটি হ'ল আমরা সর্বাধিক এক ধাপে একবারে পুনরাবৃত্তি করি ।

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