fbpx
维基百科

共递归

共递归在计算机科学重视一类操作,与递归在范畴论上对偶。因而递归是分析地工作,把数据分解为更小的数据直至达到基本情况。共递归是合成地工作,从基本情况构造出数据。共递归的数据是自己一点一点构造出来的。一个类似但不同的概念是生成式递归(generative recursion)。

共递归常与惰性求值配合,产生一个潜在无穷结构的有限子集。

参见

  • Bisimulation英语Bisimulation
  • 余归纳英语Coinduction
  • 递归
  • Anamorphism英语Anamorphism

注释

  1. ^ Not validating input data.
  2. ^ More elegantly, one can start by placing the root node itself in the structure and then iterating.
  3. ^ Post-order is to make "leaf node is base case" explicit for exposition, but the same analysis works for pre-order or in-order.
  4. ^ Breadth-first traversal, unlike depth-first, is unambiguous, and visits a node value before processing children.
  5. ^ Technically, one may define a breadth-first traversal on an ordered, disconnected set of trees – first the root node of each tree, then the children of each tree in turn, then the grandchildren in turn, etc.
  6. ^ Assume fixed branching factor (e.g., binary), or at least bounded, and balanced (infinite in every direction).
  7. ^ First defining a tree class, say via:
    class Tree: def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right def __str__(self): return str(self.value) 

    and initializing a tree, say via:

    t = Tree(1, Tree(2, Tree(4), Tree(5)), Tree(3, Tree(6), Tree(7))) 

    In this example nodes are labeled in breadth-first order:

     1 2 3 4 5 6 7 
  8. ^ Intuitively, the function iterates over subtrees (possibly empty), then once these are finished, all that is left is the node itself, whose value is then returned; this corresponds to treating a leaf node as basic.
  9. ^ Here the argument (and loop variable) is considered as a whole, possible infinite tree, represented by (identified with) its root node (tree = root node), rather than as a potential leaf node, hence the choice of variable name.

参考文献

  1. ^ Barwise and Moss 1996.
  2. ^ Moss and Danner 1997.
  3. ^ Smyth and Plotkin 1982.
  4. ^ Gibbons and Hutton 2005.
  5. ^ Doets and van Eijck 2004.
  6. ^ Leclerc and Paulin-Mohring, 1994
  7. ^ Hettinger 2009.
  8. ^ Allison 1989; Smith 2009.
  9. ^ Jones and Gibbons 1992.
  • Bird, Richard Simpson. Using circular programs to eliminate multiple traversals of data. Acta Informatica. 1984, 21 (3): 239–250. doi:10.1007/BF00264249. 
  • Lloyd Allison. Circular Programs and Self-Referential Structures. Software Practice and Experience. April 1989, 19 (2): 99–109 [2017-12-01]. doi:10.1002/spe.4380190202. (原始内容于2009-07-21). 
  • Geraint Jones and Jeremy Gibbons. Linear-time breadth-first tree algorithms: An exercise in the arithmetic of folds and zips (Technical report). Dept of Computer Science, University of Auckland. 1992 [2017-12-01]. (原始内容于2012-10-06). 
  • Jon Barwise; Lawrence S Moss. . Center for the Study of Language and Information. June 1996 [2017年12月1日]. ISBN 978-1-57586-009-1. (原始内容存档于2010年6月21日). 
  • Lawrence S Moss; Norman Danner. On the Foundations of Corecursion. Logic Journal of the IGPL. 1997, 5 (2): 231–257 [2017-12-01]. doi:10.1093/jigpal/5.2.231. (原始内容于2012-10-15). 
  • Kees Doets; Jan van Eijck. The Haskell Road to Logic, Maths, and Programming. King's College Publications. May 2004 [2017-12-01]. ISBN 978-0-9543006-9-2. (原始内容于2019-05-11). 
  • David Turner. Total Functional Programming. Journal of Universal Computer Science. 2004-07-28, 10 (7): 751–768 [2017-12-01]. doi:10.3217/jucs-010-07-0751. (原始内容于2020-11-14). 
  • Jeremy Gibbons; Graham Hutton. Proof methods for corecursive programs. Fundamenta Informaticae Special Issue on Program Transformation. April 2005, 66 (4): 353–366 [2017-12-01]. (原始内容于2015-09-09). 
  • Leon P Smith, Lloyd Allison's Corecursive Queues: Why Continuations Matter, The Monad Reader, 2009-07-29, (14): 37–68 [2017-12-01], (原始内容于2020-08-14) 
  • Raymond Hettinger. Recipe 576961: Technique for cyclical iteration. 2009-11-19 [2017-12-01]. (原始内容于2009-11-25). 
  • M. B. Smyth and G. D. Plotkin. The Category-Theoretic Solution of Recursive Domain Equations. SIAM Journal on Computing. 1982, 11 (4): 761–783. doi:10.1137/0211062. 
  • Leclerc, Francois; Paulin-Mohring, Christine. Programming with Streams in Coq: A Case Study: the Sieve of Eratosthenes. Types for Proofs and Programs: International Workshop TYPES '93. Springer-Verlag New York, Inc. 1993: 191–212. ISBN 3-540-58085-9. 

共递归, 提示, 此条目的主题不是互递归, 在计算机科学重视一类操作, 与递归在范畴论上对偶, 因而递归是分析地工作, 把数据分解为更小的数据直至达到基本情况, 是合成地工作, 从基本情况构造出数据, 的数据是自己一点一点构造出来的, 一个类似但不同的概念是生成式递归, generative, recursion, 常与惰性求值配合, 产生一个潜在无穷结构的有限子集, 已隱藏部分未翻譯内容, 歡迎參與翻譯, 目录, 例子, factorial, fibonacci, sequence, tree, traversa. 提示 此条目的主题不是互递归 共递归在计算机科学重视一类操作 与递归在范畴论上对偶 因而递归是分析地工作 把数据分解为更小的数据直至达到基本情况 共递归是合成地工作 从基本情况构造出数据 共递归的数据是自己一点一点构造出来的 一个类似但不同的概念是生成式递归 generative recursion 共递归常与惰性求值配合 产生一个潜在无穷结构的有限子集 已隱藏部分未翻譯内容 歡迎參與翻譯 目录 1 例子 1 1 Factorial 1 2 Fibonacci sequence 1 3 Tree traversal 2 Definition 3 Discussion 4 历史 5 参见 6 注释 7 参考文献 例子 编辑 Corecursion can be understood by contrast with recursion which is more familiar While corecursion is primarily of interest in functional programming it can be illustrated using imperative programming which is done below using the generator facility in Python In these examples local variables are used and assigned values imperatively destructively though these are not necessary in corecursion in pure functional programming In pure functional programming rather than assigning to local variables these computed values form an invariable sequence and prior values are accessed by self reference later values in the sequence reference earlier values in the sequence to be computed The assignments simply express this in the imperative paradigm and explicitly specify where the computations happen which serves to clarify the exposition Factorial 编辑 A classic example of recursion is computing the factorial which is defined recursively as 0 1 displaystyle 0 1 and n n n 1 displaystyle n n times n 1 To recursively compute its result on a given input a recursive function calls a copy of itself with a different smaller in some way input and uses the result of this call to construct its result The recursive call does the same unless the base case has been reached Thus a call stack develops in the process For example to compute fac 3 this recursively calls in turn fac 2 fac 1 fac 0 winding up the stack at which point recursion terminates with fac 0 1 and then the stack unwinds in reverse order and the results are calculated on the way back along the call stack to the initial call frame fac 3 where the final result is calculated as 3 2 6 and finally returned In this example a function returns a single value This stack unwinding can be explicated defining the factorial corecursively as an iterator where one starts with the case of 1 0 displaystyle 1 0 then from this starting value constructs factorial values for increasing numbers 1 2 3 as in the above recursive definition with time arrow reversed as it were by reading it backwards as n n 1 n 1 displaystyle n times n 1 n 1 The corecursive algorithm thus defined produces a stream of all factorials This may be concretely implemented as a generator Symbolically noting that computing next factorial value requires keeping track of both n and f a previous factorial value this can be represented as n f 0 1 n 1 f n 1 displaystyle n f 0 1 n 1 f times n 1 or in Haskell n f gt n 1 f n 1 iterate 0 1 meaning starting from n f 0 1 displaystyle n f 0 1 on each step the next values are calculated as n 1 f n 1 displaystyle n 1 f times n 1 This is mathematically equivalent and almost identical to the recursive definition but the 1 displaystyle 1 emphasizes that the factorial values are being built up going forwards from the starting case rather than being computed after first going backwards down to the base case with a 1 displaystyle 1 decrement Note also that the direct output of the corecursive function does not simply contain the factorial n displaystyle n values but also includes for each value the auxiliary data of its index n in the sequence so that any one specific result can be selected among them all as and when needed Note the connection with denotational semantics where the denotations of recursive programs is built up corecursively in this way In Python a recursive factorial function can be defined as a def factorial n if n 0 return 1 else return n factorial n 1 This could then be called for example as factorial 5 to compute 5 A corresponding corecursive generator can be defined as def factorials n f 0 1 while True yield f n f n 1 f n 1 This generates an infinite stream of factorials in order a finite portion of it can be produced by def n factorials k n f 0 1 while n lt k yield f n f n 1 f n 1 This could then be called to produce the factorials up to 5 via for f in n factorials 5 print f If we re only interested in a certain factorial just the last value can be taken or we can fuse the production and the access into one function def nth factorial k n f 0 1 while n lt k n f n 1 f n 1 yield f As can be readily seen here this is practically equivalent just by substituting return for the only yield there to the accumulator argument technique for tail recursion unwound into an explicit loop Thus it can be said that the concept of corecursion is an explication of the embodiment of iterative computation processes by recursive definitions where applicable Fibonacci sequence 编辑 In the same way the Fibonacci sequence can be represented as a b 0 1 b a b displaystyle a b 0 1 b a b Note that because the Fibonacci sequence is a recurrence relation of order 2 the corecursive relation must track two successive terms with the b displaystyle b corresponding to shift forward by one step and the a b displaystyle a b corresponding to computing the next term This can then be implemented as follows using parallel assignment def fibonacci sequence a b 0 1 while True yield a a b b a b In Haskell map fst a b gt b a b iterate 0 1 Tree traversal 编辑 Tree traversal via a depth first approach is a classic example of recursion Dually breadth first traversal can very naturally be implemented via corecursion Without using recursion or corecursion one may traverse a tree by starting at the root node placing the child nodes in a data structure then removing the nodes in the data structure in turn and iterating over its children b If the data structure is a stack LIFO this yields depth first traversal while if the data structure is a queue FIFO this yields breadth first traversal Using recursion a post order c depth first traversal can be implemented by starting at the root node and recursively traversing each child subtree in turn the subtree based at each child node the second child subtree does not start processing until the first child subtree is finished Once a leaf node is reached or the children of a branch node have been exhausted the node itself is visited e g the value of the node itself is outputted In this case the call stack of the recursive functions acts as the stack that is iterated over Using corecursion a breadth first traversal can be implemented by starting at the root node outputting its value d then breadth first traversing the subtrees i e passing on the whole list of subtrees to the next step not a single subtree as in the recursive approach at the next step outputting the value of all of their root nodes then passing on their child subtrees etc e In this case the generator function indeed the output sequence itself acts as the queue As in the factorial example above where the auxiliary information of the index which step one was at n was pushed forward in addition to the actual output of n in this case the auxiliary information of the remaining subtrees is pushed forward in addition to the actual output Symbolically v t FullTree RootValues ChildTrees meaning that at each step one outputs the list of values of root nodes then proceeds to the child subtrees Generating just the node values from this sequence simply requires discarding the auxiliary child tree data then flattening the list of lists values are initially grouped by level depth flattening ungrouping yields a flat linear list These can be compared as follows The recursive traversal handles a leaf node at the bottom as the base case when there are no children just output the value and analyzes a tree into subtrees traversing each in turn eventually resulting in just leaf nodes actual leaf nodes and branch nodes whose children have already been dealt with cut off below By contrast the corecursive traversal handles a root node at the top as the base case given a node first output the value treats a tree as being synthesized of a root node and its children then produces as auxiliary output a list of subtrees at each step which are then the input for the next step the child nodes of the original root are the root nodes at the next step as their parents have already been dealt with cut off above Note also that in the recursive traversal there is a distinction between leaf nodes and branch nodes while in the corecursive traversal there is no distinction as each node is treated as the root node of the subtree it defines Notably given an infinite tree f the corecursive breadth first traversal will traverse all nodes just as for a finite tree while the recursive depth first traversal will go down one branch and not traverse all nodes and indeed if traversing post order as in this example or in order it will visit no nodes at all because it never reaches a leaf This shows the usefulness of corecursion rather than recursion for dealing with infinite data structures In Python this can be implemented as follows g The usual post order depth first traversal can be defined as h def df node if node is not None df node left df node right print node value This can then be called by df t to print the values of the nodes of the tree in post order depth first order The breadth first corecursive generator can be defined as i def bf tree tree list tree while tree list new tree list for tree in tree list if tree is not None yield tree value new tree list append tree left new tree list append tree right tree list new tree list This can then be called to print the values of the nodes of the tree in breadth first order for i in bf t print i Definition 编辑 Initial data types can be defined as being the least fixpoint up to isomorphism of some type equation the isomorphism is then given by an initial algebra Dually final or terminal data types can be defined as being the greatest fixpoint of a type equation the isomorphism is then given by a final coalgebra If the domain of discourse is the category of sets and total functions then final data types may contain infinite non wellfounded values whereas initial types do not 1 2 On the other hand if the domain of discourse is the category of complete partial orders and continuous functions which corresponds roughly to the Haskell programming language then final types coincide with initial types and the corresponding final coalgebra and initial algebra form an isomorphism 3 Corecursion is then a technique for recursively defining functions whose range codomain is a final data type dual to the way that ordinary recursion recursively defines functions whose domain is an initial data type 4 The discussion below provides several examples in Haskell that distinguish corecursion Roughly speaking if one were to port these definitions to the category of sets they would still be corecursive This informal usage is consistent with existing textbooks about Haskell 5 Also note that the examples used in this article predate the attempts to define corecursion and explain what it is Discussion 编辑 The rule for primitive corecursion on codata is the dual to that for primitive recursion on data Instead of descending on the argument by pattern matching on its constructors that were called up before somewhere so we receive a ready made datum and get at its constituent sub parts i e fields we ascend on the result by filling in its destructors or observers that will be called afterwards somewhere so we re actually calling a constructor creating another bit of the result to be observed later on Thus corecursion creates potentially infinite codata whereas ordinary recursion analyses necessarily finite data Ordinary recursion might not be applicable to the codata because it might not terminate Conversely corecursion is not strictly necessary if the result type is data because data must be finite In Programming with streams in Coq a case study the Sieve of Eratosthenes 6 we find hd conc a s a tl conc a s s sieve p s if div p hd s then sieve p tl s else conc hd s sieve p tl s hd primes s hd s tl primes s primes sieve hd s tl s where primes are obtained by applying the primes operation to the stream Enu 2 Following the above notation the sequence of primes with a throwaway 0 prefixed to it and numbers streams being progressively sieved can be represented as p s 0 2 h d s s i e v e h d s t l s displaystyle p s 0 2 hd s sieve hd s tl s or in Haskell p s h t gt h sieve h t iterate 0 2 The authors discuss how the definition of sieve is not guaranteed always to be productive and could become stuck e g if called with 5 10 as the initial stream Here is another example in Haskell The following definition produces the list of Fibonacci numbers in linear time fibs 0 1 zipWith fibs tail fibs This infinite list depends on lazy evaluation elements are computed on an as needed basis and only finite prefixes are ever explicitly represented in memory This feature allows algorithms on parts of codata to terminate such techniques are an important part of Haskell programming This can be done in Python as well 7 from itertools import tee chain islice imap def add x y return x y def fibonacci def deferred output for i in output yield i result c1 c2 tee deferred output 3 paired imap add c1 islice c2 1 None output chain 0 1 paired return result for i in islice fibonacci 20 print i The definition of zipWith can be inlined leading to this fibs 0 1 next fibs where next a t b a b next t This example employs a self referential data structure Ordinary recursion makes use of self referential functions but does not accommodate self referential data However this is not essential to the Fibonacci example It can be rewritten as follows fibs fibgen 0 1 fibgen x y x fibgen y x y This employs only self referential function to construct the result If it were used with strict list constructor it would be an example of runaway recursion but with non strict list constructor this guarded recursion gradually produces an indefinitely defined list Corecursion need not produce an infinite object a corecursive queue 8 is a particularly good example of this phenomenon The following definition produces a breadth first traversal of a binary tree in linear time data Tree a b Leaf a Branch b Tree a b Tree a b bftrav Tree a b gt Tree a b bftrav tree queue where queue tree gen 1 queue gen 0 p gen len Leaf s gen len 1 s gen len Branch l r s l r gen len 1 s This definition takes an initial tree and produces a list of subtrees This list serves dual purpose as both the queue and the result gen len p produces its output len notches after its input back pointer p along the queue It is finite if and only if the initial tree is finite The length of the queue must be explicitly tracked in order to ensure termination this can safely be elided if this definition is applied only to infinite trees Another particularly good example gives a solution to the problem of breadth first labeling 9 The function label visits every node in a binary tree in a breadth first fashion and replaces each label with an integer each subsequent integer is bigger than the last by one This solution employs a self referential data structure and the binary tree can be finite or infinite label Tree a b gt Tree Int Int label t t where t ns go t 1 ns go Tree a b gt Int gt Tree Int Int Int go Leaf n ns Leaf n n 1 ns go Branch l r n ns Branch n l r n 1 ns where l ns go l ns r ns go r ns An apomorphism such as an anamorphism such as unfold is a form of corecursion in the same way that a paramorphism such as a catamorphism such as fold is a form of recursion The Coq proof assistant supports corecursion and coinduction using the CoFixpoint command 历史 编辑 Corecursion referred to as circular programming dates at least to Bird 1984 who credits John Hughes and Philip Wadler more general forms were developed in Allison 1989 The original motivations included producing more efficient algorithms allowing 1 pass over data in some cases instead of requiring multiple passes and implementing classical data structures such as doubly linked lists and queues in functional languages 参见 编辑Bisimulation 英语 Bisimulation 余归纳 英语 Coinduction 递归 Anamorphism 英语 Anamorphism 注释 编辑 Not validating input data More elegantly one can start by placing the root node itself in the structure and then iterating Post order is to make leaf node is base case explicit for exposition but the same analysis works for pre order or in order Breadth first traversal unlike depth first is unambiguous and visits a node value before processing children Technically one may define a breadth first traversal on an ordered disconnected set of trees first the root node of each tree then the children of each tree in turn then the grandchildren in turn etc Assume fixed branching factor e g binary or at least bounded and balanced infinite in every direction First defining a tree class say via class Tree def init self value left None right None self value value self left left self right right def str self return str self value and initializing a tree say via t Tree 1 Tree 2 Tree 4 Tree 5 Tree 3 Tree 6 Tree 7 In this example nodes are labeled in breadth first order 1 2 3 4 5 6 7 Intuitively the function iterates over subtrees possibly empty then once these are finished all that is left is the node itself whose value is then returned this corresponds to treating a leaf node as basic Here the argument and loop variable is considered as a whole possible infinite tree represented by identified with its root node tree root node rather than as a potential leaf node hence the choice of variable name 参考文献 编辑 Barwise and Moss 1996 Moss and Danner 1997 Smyth and Plotkin 1982 Gibbons and Hutton 2005 Doets and van Eijck 2004 Leclerc and Paulin Mohring 1994 Hettinger 2009 Allison 1989 Smith 2009 Jones and Gibbons 1992 Bird Richard Simpson Using circular programs to eliminate multiple traversals of data Acta Informatica 1984 21 3 239 250 doi 10 1007 BF00264249 Lloyd Allison Circular Programs and Self Referential Structures Software Practice and Experience April 1989 19 2 99 109 2017 12 01 doi 10 1002 spe 4380190202 原始内容存档于2009 07 21 Geraint Jones and Jeremy Gibbons Linear time breadth first tree algorithms An exercise in the arithmetic of folds and zips Technical report Dept of Computer Science University of Auckland 1992 2017 12 01 原始内容存档于2012 10 06 Jon Barwise Lawrence S Moss Vicious Circles Center for the Study of Language and Information June 1996 2017年12月1日 ISBN 978 1 57586 009 1 原始内容存档于2010年6月21日 Lawrence S Moss Norman Danner On the Foundations of Corecursion Logic Journal of the IGPL 1997 5 2 231 257 2017 12 01 doi 10 1093 jigpal 5 2 231 原始内容存档于2012 10 15 Kees Doets Jan van Eijck The Haskell Road to Logic Maths and Programming King s College Publications May 2004 2017 12 01 ISBN 978 0 9543006 9 2 原始内容存档于2019 05 11 David Turner Total Functional Programming Journal of Universal Computer Science 2004 07 28 10 7 751 768 2017 12 01 doi 10 3217 jucs 010 07 0751 原始内容存档于2020 11 14 Jeremy Gibbons Graham Hutton Proof methods for corecursive programs Fundamenta Informaticae Special Issue on Program Transformation April 2005 66 4 353 366 2017 12 01 原始内容存档于2015 09 09 Leon P Smith Lloyd Allison s Corecursive Queues Why Continuations Matter The Monad Reader 2009 07 29 14 37 68 2017 12 01 原始内容存档于2020 08 14 Raymond Hettinger Recipe 576961 Technique for cyclical iteration 2009 11 19 2017 12 01 原始内容存档于2009 11 25 M B Smyth and G D Plotkin The Category Theoretic Solution of Recursive Domain Equations SIAM Journal on Computing 1982 11 4 761 783 doi 10 1137 0211062 Leclerc Francois Paulin Mohring Christine Programming with Streams in Coq A Case Study the Sieve of Eratosthenes Types for Proofs and Programs International Workshop TYPES 93 Springer Verlag New York Inc 1993 191 212 ISBN 3 540 58085 9 取自 https zh wikipedia org w index php title 共递归 amp oldid 68263043, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。