首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何共享延续的中间结果?

如何共享延续的中间结果?
EN

Stack Overflow用户
提问于 2019-08-02 03:56:11
回答 2查看 268关注 0票数 8

请注意,尽管这个问题中的示例是用Javascript编码的,但基本概念在Haskell和我中都很常见,虽然我更喜欢用Javascript来表达我自己,但我也喜欢用Haskell来回答。

在Javascript中,我使用CPS根据一元原则处理异步计算。然而,为了简单起见,我将使用普通的延续单体来回答这个问题。

一旦我的延续组合增长,我就会发现自己处于需要访问这些组合的中间结果的情况下。因为Javascript是命令式的,所以很容易将这样的结果存储在变量中并在以后访问它们。但是,由于我们讨论的是延续,访问中间结果意味着调用函数并多次访问它们,这意味着大量的重新计算。

这似乎非常适合于记忆。但是,如果一个函数不返回任何东西,而只是调用它的continuation (和btw ),我如何才能记住该函数的返回值。正如我之前提到的,我使用的异步函数在Javascript的事件循环的当前循环中也不返回任何内容)。

似乎我必须提取正确的续集。也许通过shift/reset使用分隔的延续是可能的,但我不知道如何应用这些组合符。这个问题可能并不难解决,我只是被连续通过style...so这块神奇的土地搞糊涂了,请纵容我。

下面是一个在Javascript中没有记忆的Cont的简化示例:

代码语言:javascript
复制
const taggedLog = tag => s =>
  (console.log(tag, s), s);

const id = x => x;

const Cont = k => ({
  runCont: k,
  [Symbol.toStringTag]: "Cont"
});

const contAp = tf => tx =>
  Cont(k => tf.runCont(f => tx.runCont(x => k(f(x)))));

const contLiftA2 = f => tx => ty =>
  contAp(contMap(f) (tx)) (ty);

const contOf = x => Cont(k => k(x));

const contMap = f => tx =>
  Cont(k => tx.runCont(x => k(f(x))));
                                  
const contReset = tx => // delimited continuations
  contOf(tx.runCont(id));

const contShift = f => // delimited continuations
  Cont(k => f(k).runCont(id));

const inc = contMap(x => taggedLog("eval inc") (x + 1));
const inc2 = inc(contOf(2));
const inc3 = inc(contOf(3));
const add = contLiftA2(x => y => taggedLog("eval add") (x + y));
const mul = contLiftA2(x => y => taggedLog("eval mul") (x * y));

const intermediateResult = add(inc2) (inc3);

mul(intermediateResult) (intermediateResult).runCont(id);

/*
  should only log four lines:
  eval inc 3
  eval inc 4
  eval add 7
  eval mul 49
*/

EN

回答 2

Stack Overflow用户

发布于 2019-08-02 04:49:31

您的问题似乎是您的Cont还没有monad实现。这样,访问以前的结果就非常简单了--它们只是嵌套的延续回调的作用域(作为常量):

代码语言:javascript
复制
const contChain = tx => f =>
  Cont(k => tx.runCont(r => f(r).runCont(k)));

contChain( add(inc2) (inc3), intermediateResult => {
  const intermediateCont = contOf(intermediateResult);
  return mul(intermediateCont) (intermediateCont);
}).runCont(id);

(当然这有点奇怪,你的所有函数都已经被提升了,并将Cont值作为参数-它们不应该这样做,只是简单地使用return Cont值的函数)

Haskell中的代码:

代码语言:javascript
复制
import Control.Monad.Cont
import Control.Applicative

let inc = liftA (+1)
let inc2 = inc $ return 2
let inc3 = inc $ return 3
let add = liftA2 (+)
let mul = liftA2 (*)

(`runCont` id) $ add inc2 inc3 >>= \intermediateResult ->
  let intermediateCont = return intermediateResult
  in mul intermediateCont intermediateCont
-- 49

{- or with do notation: -}

(`runCont` id) $ do
  intermediateResult <- add inc2 inc3
  let intermediateCont = return intermediateResult
  mul intermediateCont intermediateCont
-- 49

(我还没有使用monad transformers来产生taggedLog副作用)

票数 8
EN

Stack Overflow用户

发布于 2019-08-02 19:02:12

为了获得想要的行为,我似乎无法避免变得不纯洁。不过,杂质只是局部的,因为我只是用它的结果值替换了延续链。我可以在不改变程序行为的情况下做到这一点,因为这正是引用透明性所保证的。

下面是Cont构造函数的转换:

代码语言:javascript
复制
const Cont = k => ({
  runCont: k,
  [Symbol.toStringTag]: "Cont"
});

// becomes

const Cont = k => thisify(o => { // A
    o.runCont = (res, rej) => k(x => { // B
      o.runCont = l => l(x); // C
      return res(x); // D
    }, rej); // E

    o[Symbol.toStringTag] = "Cont";
    return o;
  });

A行中的thisify只是模拟this上下文,以便要构造的Object知道它自己。

代码行B是决定性的变化:在使用x (< Task >d15B>)调用res之前,我构造了另一个lambda,它将结果代码包装在当前Task对象(C)的runTask属性下,而不是仅仅将res传递给延续代码。

在出现错误的情况下,像往常一样(E)将rej应用于x

下面是上面的运行示例,现在可以正常运行了:

代码语言:javascript
复制
const taggedLog = pre => s =>
  (console.log(pre, s), s);

const id = x => x;

const thisify = f => f({}); // mimics this context

const Cont = k => thisify(o => {
    o.runCont = (res, rej) => k(x => {
      o.runCont = l => l(x);
      return res(x);
    }, rej);
    
    o[Symbol.toStringTag] = "Cont";
    return o;
  });

const contAp = tf => tx =>
  Cont(k => tf.runCont(f => tx.runCont(x => k(f(x)))));

const contLiftA2 = f => tx => ty =>
  contAp(contMap(f) (tx)) (ty);

const contOf = x => Cont(k => k(x));

const contMap = f => tx =>
  Cont(k => tx.runCont(x => k(f(x))));
                                  
const inc = contMap(x => taggedLog("eval inc") (x + 1));
const inc2 = inc(contOf(2));
const inc3 = inc(contOf(3));
const add = contLiftA2(x => y => taggedLog("eval add") (x + y));
const mul = contLiftA2(x => y => taggedLog("eval mul") (x * y));

const intermediateResult = add(inc2) (inc3);

mul(intermediateResult) (intermediateResult).runCont(id);

/* should merely log
eval inc 3
eval inc 4
eval add 7
eval add 49
*/

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57316172

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档