কার্যকরী প্রোগ্রামিং এবং রাষ্ট্রীয় আলগোরিদিম


12

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

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

উদাহরণ হিসেবে বলা যায়, এখানে, মূল্যায়নের reachability এর থেকে একটি , আমি বাদ দেওয়ার আছে চাই উভয় মাধ্যমে পাথ পরীক্ষা করার সময় এবং :

একটি অটোমেটনের প্রতিনিধিত্বকারী ডিগ্রাফ

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

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

আমার উদাহরণস্বরূপ মামলার বৈধতা ছাড়াও , এই জাতীয় সমস্যাগুলি সমাধান করার জন্য আর কোন কৌশল উপলব্ধ? আমি মনে করি এই সাধারণ যথেষ্ট হতে হবে মত সেখানে কি সঙ্গে ঘটে মত সমাধান হতে হবে যে fold*বা map

এখনও অবধি, learnyouahaskell.com পড়তে আমার কোনও সন্ধান পেল না, তবে বিবেচনা করুন আমি এখনও মনদেখাকে স্পর্শ করি নি।

( যদি আগ্রহী হয়, আমি কোড কোডভিউতে আমার কোড পোস্ট করেছি )


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

1
@ পাথারিয়েনস ফ্ল্যাম আপনার আগ্রহের জন্য আপনাকে ধন্যবাদ! কোডটি এখানে , পুরো প্রকল্পের একটি লিঙ্কও রয়েছে। আমি এ পর্যন্ত যা করেছি তার সাথে আমি ইতিমধ্যে বিভ্রান্ত হয়ে পড়েছি, তাই হ্যাঁ, উন্নত কৌশলগুলি না
দেখাই ভাল

1
একটি রাষ্ট্র অটোমেটা কার্যকরী প্রোগ্রামিংয়ের বিরোধী pretty কার্যকরী প্রোগ্রামিং হ'ল অভ্যন্তরীণ অবস্থা ব্যতীত সমস্যাগুলি সমাধান করার বিষয়ে, যখন একটি রাষ্ট্রীয় অটোমাতা তার নিজস্ব রাষ্ট্র পরিচালনার বিষয়ে।
ফিলিপ

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

5
@ ফিলিপ: ফাংশনাল প্রোগ্রামিং রাষ্ট্রকে স্পষ্ট করে তোলার বিষয়ে, এটি নিষিদ্ধ করার বিষয়ে নয়। প্রকৃতপক্ষে, গোটোসে পূর্ণ those স্টেট মেশিনগুলি বাস্তবায়নের জন্য লেজ পুনরাবৃত্তি সত্যই দুর্দান্ত সরঞ্জাম।
hugomg

উত্তর:


16

কার্যকরী প্রোগ্রামিং রাষ্ট্র থেকে মুক্তি পাবে না। এটি কেবল এটি স্পষ্ট করে তোলে! যদিও এটি সত্য যে মানচিত্রের মতো ফাংশনগুলি প্রায়শই "ভাগ করা" ডেটা স্ট্রাকচারকে "উদ্ঘাটন" করে দেয়, আপনি যা করতে চান তা যদি কোনও পুনঃব্যবহারযোগ্যতা অ্যালগরিদম লিখতে হয় তবে আপনি ইতিমধ্যে কোন নোডগুলি পরিদর্শন করেছেন সেটি ট্র্যাক করার বিষয়টি কেবল:

import qualified Data.Set as S
data Node = Node Int [Node] deriving (Show)

-- Receives a root node, returns a list of the node keyss visited in a depth-first search
dfs :: Node -> [Int]
dfs x = fst (dfs' (x, S.empty))

-- This worker function keeps track of a set of already-visited nodes to ignore.
dfs' :: (Node, S.Set Int) -> ([Int], S.Set Int)
dfs' (node@(Node k ns), s )
  | k  `S.member` s = ([], s)
  | otherwise =
    let (childtrees, s') = loopChildren ns (S.insert k s) in
    (k:(concat childtrees), s')

--This function could probably be implemented as just a fold but Im lazy today...
loopChildren :: [Node] -> S.Set Int -> ([[Int]], S.Set Int)
loopChildren []  s = ([], s)
loopChildren (n:ns) s =
  let (xs, s') = dfs' (n, s) in
  let (xss, s'') = loopChildren ns s' in
  (xs:xss, s'')

na = Node 1 [nb, nc, nd]
nb = Node 2 [ne]
nc = Node 3 [ne, nf]
nd = Node 4 [nf]
ne = Node 5 [ng]
nf = Node 6 []
ng = Node 7 []

main = print $ dfs na -- [1,2,5,7,3,6,4]

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


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

রাষ্ট্রীয় ভাষায়, ডিএফএস এই জাতীয় চেহারা দেখায়:

visited = set()  #mutable state
visitlist = []   #mutable state
def dfs(node):
   if isMember(node, visited):
       //do nothing
   else:
       visited[node.key] = true           
       visitlist.append(node.key)
       for child in node.children:
         dfs(child)

পরিবর্তনশীল অবস্থা থেকে মুক্তি পাওয়ার জন্য এখন আমাদের একটি উপায় খুঁজে বের করা উচিত। প্রথমে আমরা "ভিজিটলিস্ট" ভেরিয়েবলকে ডিএফএসের মাধ্যমে ফিরিয়ে আনব যা শূন্য করার পরিবর্তে:

visited = set()  #mutable state
def dfs(node):
   if isMember(node, visited):
       return []
   else:
       visited[node.key] = true
       return [node.key] + concat(map(dfs, node.children))

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

let increment_state s = s+1 in
let extract_state s = (s, 0) in

let s0 = 0 in
let s1 = increment_state s0 in
let s2 = increment_state s1 in
let (x, s3) = extract_state s2 in
-- and so on...

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

def dfs(node, visited1):
   if isMember(node, visited1):
       return ([], visited1) #return the old state because we dont want to  change it
   else:
       curr_visited = insert(node.key, visited1) #immutable update, with a new variable for the new value
       childtrees = []
       for child in node.children:
          (ct, curr_visited) = dfs(child, curr_visited)
          child_trees.append(ct)
       return ([node.key] + concat(childTrees), curr_visited)

হাস্কেল সংস্করণটি আমি এখানে যা করেছি তা বেশ কিছু করে, কেবল এটি পুরোপুরি চলে এবং পরিবর্তিত "curr_visited" এবং "শিশুসন্তান" ভেরিয়েবলের পরিবর্তে একটি অভ্যন্তরীণ পুনরাবৃত্তির ফাংশন ব্যবহার করে।


মোনাডদের ক্ষেত্রে, তারা মূলত যা সম্পাদন করে তা হস্তান্তর করে আপনার হাতের সাহায্যে জোর করে পরিবর্তে "curr_visited" চারপাশে পাস করছে। এটি কেবল কোড থেকে বিশৃঙ্খলা দূর করে না, বরং এটি আপনাকে ভুল করা থেকেও বাধা দেয় যেমন ফোরকিং স্টেট (একই "দেখা" কে রাষ্ট্রের শৃঙ্খলার পরিবর্তে দুটি পরবর্তী কলগুলিতে সেট করা)।


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

@ বিগস্টোনস: আমার মনে হয় আপনারা আমার কোডটি সোনাদের মোকাবেলা করার আগে কীভাবে কাজ করে তা বোঝার চেষ্টা করা উচিত - তারা মূলত আমি একই কাজটি করব তবে আপনাকে বিভ্রান্ত করার জন্য বিমূর্ততার অতিরিক্ত স্তর সহ। যাইহোক, আমি জিনিসগুলি আরও পরিষ্কার করার চেষ্টা করার জন্য কিছু অতিরিক্ত ব্যাখ্যা যুক্ত করেছি
hugomg

1
"কার্যকরী প্রোগ্রামিং রাষ্ট্র থেকে মুক্তি পাবে না It এটি কেবল এটিকে স্পষ্ট করে তোলে!": এটি সত্যই স্পষ্ট করছে!
জর্জিও

"[মনডস] আপনাকে রাষ্ট্রের পরিবর্তনশীলকে পুরোপুরি সুস্পষ্টভাবে পাস করতে দেয় এবং ইন্টারফেস গ্যারান্টি দেয় যে এটি একক থ্রেডযুক্ত পদ্ধতিতে ঘটেছে" <- এটি মনাদের একটি আলোকিত বর্ণনা; এই প্রশ্নের প্রসঙ্গে বাইরে আমি 'স্টোর ভেরিয়েবল'কে' ক্লোজার 'দিয়ে প্রতিস্থাপন করতে পারি
অ্যানথ্রপিক অ্যান্ড্রয়েড

2

এখানে নির্ভর একটি সহজ উত্তর mapConcat

 mapConcat :: (a -> [b]) -> [a] -> [b]
 -- mapConcat is in the std libs, mapConcat = concat . map
 type Path = []

 isReachable :: a -> Auto a -> a -> [Path a]
 isReachable to auto from | to == from = [[]]
 isReachable to auto from | otherwise = 
    map (from:) . mapConcat (isReachable to auto) $ neighbors auto from

যেখানে neighborsরাজ্যগুলি তত্ক্ষণাত্ একটি রাষ্ট্রের সাথে সংযুক্ত হয়ে ফিরে আসে। এটি কয়েকটি সিরিজের পথ ফেরায়।

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