我对代数数据类型的经验很少,因为我使用一种没有本地支持的语言。通常可以使用延续传递风格来获得远程相似的体验,但处理 CPS 编码类型不太舒服。
考虑到这一点,为什么像 Parsec 这样的库会使用 CPS?
newtype ParsecT s u m a
= ParsecT {unParser :: forall b .
State s u
-> (a -> State s u -> ParseError -> m b) -- consumed ok
-> (ParseError -> m b) -- consumed err
-> (a -> State s u -> ParseError -> m b) -- empty ok
-> (ParseError -> m b) -- empty err
-> m b
}
Run Code Online (Sandbox Code Playgroud)
一个线索是try解析器,它通过在两种情况下传递空错误延续来排除消耗的错误情况:
try :: …Run Code Online (Sandbox Code Playgroud) haskell functional-programming algebraic-data-types continuation-passing
我最近一直在重写许多OCaml标准库函数,以便进行尾递归.鉴于这需要直接进行CPS转换,我仍然不知道为什么默认版本不是这样编写的.
例如,在标准库中,map定义为:
let rec map f = function
[] -> []
| a::l -> let r = f a in r :: map f l
Run Code Online (Sandbox Code Playgroud)
我把它重写为:
let map f l =
let rec aux l k = match l with
[] -> k []
| a::l -> aux l (fun rest -> k (f a :: rest))
in aux l (fun x -> x)
Run Code Online (Sandbox Code Playgroud) 这是来自Clojure的Joy,第2版.http://www.manning.com/fogus2/
(defn mk-cps [accept? kend kont]
(fn [n]
((fn [n k]
(let [cont (fn [v] (k ((partial kont v) n)))]
(if (accept? n)
(k 1)
(recur (dec n) cont))))
n kend)))
Run Code Online (Sandbox Code Playgroud)
然后做一个阶乘:
(def fac (mk-cps zero? identity #(* %1 %2)))
Run Code Online (Sandbox Code Playgroud)
我的理解:
partial而不是简单地写(k (cont v n))?accept?函数通过,则完成递归,应用于k1.recur重复返回 …有没有办法链接功能withCString?我的意思是任何看起来像的功能f :: Foo -> (CFoo -> IO a) -> IO a.
例如,假设有一个功能 cFunc :: CString -> CFoo -> CBar -> IO ()
Usualy,我会做类似的事情:
haskellFunc string foo bar =
withCString string $ \ cString ->
withCFoo foo $ \ cFoo ->
withCBar bar $ \ cBar ->
cFunc cString cFoo cBar
Run Code Online (Sandbox Code Playgroud)
但我想做的事情如下:
haskellFunc = (withCString |.| withCFoo |.| withCBar) cFunc
Run Code Online (Sandbox Code Playgroud)
与一些合适的组合操作员|.|.
我正在编写带有大量C绑定的库,这个样板经常出现.难道我做错了什么?
continuations haskell function-composition continuation-passing
(后来的访问者:这个问题的两个答案都给出了很好的见解,如果你感兴趣的话,你应该把它们都读出来,我只能把它作为SO的限制除外)
从我在网上找到的关于延续monad的所有讨论,他们要么提到它如何与一些琐碎的例子一起使用,要么他们解释它是一个基本的构建块,因为在这篇关于所有monad的母亲的文章是延续monad.
我想知道是否有适用范围.我的意思是,在continuation monad中包装递归函数或相互递归是否有意义?它有助于提高可读性吗?
这是从这篇SO帖子中获取的延续模式的F#版本:
type ContinuationMonad() =
member this.Bind (m, f) = fun c -> m (fun a -> f a c)
member this.Return x = fun k -> k x
let cont = ContinuationMonad()
Run Code Online (Sandbox Code Playgroud)
仅仅是为了学术兴趣,例如帮助理解monad或计算构建者?或者是否存在一些真实的适用性,增加了类型安全性,还是避免了难以解决的典型编程问题?
即,来自Ryan Riley的call/cc的延续monad表明处理异常很复杂,但它没有解释它试图解决的问题,并且示例没有说明为什么它需要特定的monad.不可否认,我只是不明白它的作用,但它可能是一个宝库!
(注意:我对理解延续monad是如何工作不感兴趣,我认为我对它有一个很好的掌握,我只是看不出它解决了什么编程问题.)
monads f# haskell computation-expression continuation-passing
我一直在努力理解continuation/CPS,并且从我可以收集的内容中建立一个延迟计算,一旦我们到达列表的末尾,我们就会调用最终的计算.
我不明白的是,为什么CPS会阻止堆栈溢出,因为它看起来类似于按照示例1中的天真方法构建嵌套函数.对于长篇文章感到抱歉但是试图表明这个想法(可能在哪里出错)基本:
所以:
let list1 = [1;2;3]
Run Code Online (Sandbox Code Playgroud)
例1:"天真的方法"
let rec sumList = function
|[] -> 0
|h::t -> h + sumList t
Run Code Online (Sandbox Code Playgroud)
因此,当它运行时,迭代地导致:
1 + sumList [2;3]1 + (2 + sumList [3])1 + (2 + (3 + 0))因此,Tail Recursion可以克服嵌套(和溢出问题) - 运行累加器即
"示例2:尾递归"
let sumListACC lst =
let rec loop l acc =
match l with
|[] -> acc
|h::t -> loop t (h + acc)
loop lst 0
Run Code Online (Sandbox Code Playgroud)
即
sumList[2;3] (1+0)sumList[3] (2+1)sumList[] (3+3)因此,因为在每一步都评估累加器,所以没有嵌套,我们避免破坏堆栈.明确!
接下来是CPS,我知道当我们已经有一个累加器时这是必需的但是函数不是尾递归的,例如使用Foldback.虽然在上面的示例中不需要,但将CPS应用于此问题会给出: …
我是Scala的新手,并试图绕过我试图重现yield returnC#语句的延续.
在这篇文章之后,我写了以下代码:
package com.company.scalatest
import scala.util.continuations._;
object GenTest {
val gen = new Generator[Int] {
def produce = {
yieldValue(1)
yieldValue(2)
yieldValue(3)
yieldValue(42)
}
}
// Does not compile :(
// val gen2 = new Generator[Int] {
// def produce = {
// var ints = List(1, 2, 3, 42);
//
// ints.foreach((theInt) => yieldValue(theInt));
// }
// }
// But this works?
val gen3 = new Generator[Int] {
def produce = {
var ints …Run Code Online (Sandbox Code Playgroud) 如果像lambda演算或Ocaml这样的curry语言中的CPS如何有意义?从技术上讲,所有函数都有一个参数.所以说我们在一种语言中添加了CPS版本:
cps-add k n m = k ((+) n m)
Run Code Online (Sandbox Code Playgroud)
我们称之为
(cps-add random-continuation 1 2)
Run Code Online (Sandbox Code Playgroud)
这与以下相同:
(((cps-add random-continuation) 1) 2)
Run Code Online (Sandbox Code Playgroud)
我已经看到两个调用不是尾调用,实际上是一个复杂的嵌套表达式,(cps-add random-continuation)返回一个值,即一个消耗数字的函数,然后返回一个消耗另一个数字的函数,然后将两者的总和传递给那个random-continuation.但是我们不能通过简单地将它转换为CPS来解决这个值,因为我们只能给每个函数一个参数.我们需要至少有两个为继续和"实际"论证腾出空间.
还是我完全错过了什么?
但是,我想知道你是否可以互相表达?我见过的所有CPS风格的语言都有一个显式的FIX构造来定义递归.
FIX?如果是这样,你知道这样的证据吗?我希望类似Y-combinator的CPS函数CPSY可以这样工作:如果我定义了一个Y-ready CPS函数,例如:
function Yready(this, return) =
return (lambda <args> . <body using 'this' as recursion>);
Run Code Online (Sandbox Code Playgroud)
然后我会把它放进CPSY去生成一个自我修复的函数:
function CPSY(F, return) = ?????
CPSY(Yready,
lambda rec . <body where 'rec' names 'lambda <args>' from above, but with the loop closed>
)
Run Code Online (Sandbox Code Playgroud)
本CPSY应该是没有本身依赖于任何递归纯延续传递形式的函数.Y-combinator可以在普通lambda演算中以这种方式定义而无需内置递归.CPS中是否也可以以某种形式存在?
重申澄清:我正在寻找类似组合器的功能CPSY:
CPSY)scheme functional-programming continuation-passing fixpoint-combinators
注1:这里的CPS代表“持续通过风格”
我对了解如何挂接到C#异步机制非常感兴趣。基本上,据我了解的C#异步/等待功能,编译器将执行CPS转换,然后将转换后的代码传递给上下文对象,该对象管理各个线程上的任务调度。
您是否认为可以利用该编译器功能来创建功能强大的组合器,同时又保留默认的线程方面?
一个例子就是可以递归和记忆诸如
async MyTask<BigInteger> Fib(int n) // hypothetical example
{
if (n <= 1) return n;
return await Fib(n-1) + await Fib(n-2);
}
Run Code Online (Sandbox Code Playgroud)
我设法做到这一点:
void Fib(int n, Action<BigInteger> Ret, Action<int, Action<BigInteger>> Rec)
{
if (n <= 1) Ret(n);
else Rec(n-1, x => Rec(n-2, y => Ret(x + y)));
}
Run Code Online (Sandbox Code Playgroud)
(不使用异步,非常笨拙...)
或使用monad(While<X> = Either<X, While<X>>)
While<X> Fib(int n) => n <= 1 ?
While.Return((BigInteger) n) :
from x in Fib(n-1)
from y in Fib(n-2) …Run Code Online (Sandbox Code Playgroud) c# stack-overflow combinators continuation-passing async-await