我真的不知道如何描述这个问题,跟函数式编程有关,求助 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
notcome
V2EX    Node.js

我真的不知道如何描述这个问题,跟函数式编程有关,求助

  •  
  •   notcome 2014-04-19 09:33:31 +08:00 5447 次点击
    这是一个创建于 4196 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我打算撸一个 PEG based Markdown Parser,练习一下。
    然后我想写这么一个东西,注意 var = BIContent 一行:

    function BIGen (token) {
    var BICOntent= R['/']([token, R['s']([Char, BIContent])]);
    return R['s']([token, Char, BIContent]);
    }

    R['s'] 的参数为一个函数数组,返回一个函数,然后 R['/'] 的参数也是一个函数数组,返回一个函数。我现在希望实现一个递归,但是先求参数值的原因,所以……卡住了。

    怎么办?

    (我好像记得 SICP 中有有关内容,马上看看去)
    第 1 条附言    2014-04-19 15:55:19 +08:00
    function series (first, second) {
    return function (input, index) {
    var result = first(input, index);
    if (result) return result;
    result = second(input, index);
    return {output: result.output, index: result.index};
    }
    }

    嗯, 简化一下模型就是这个样子, 我希望能定义一个函数 f = series(g, f), 用正常的办法来写也就是:

    function second (input, index) {
    var result = first(input, index);
    if (result) return result;
    return second(input, index);
    }

    有什么好的办法? 因为不是每次都要调用自身, 或者这个 series 长度可能会很长, 所以想抽象一下.
    第 2 条附言    2014-04-22 10:00:05 +08:00
    感谢各位帮忙,也为自己之前描述问题不清楚感到抱歉。

    是这样的,我正在学习 PEG (Parsing Expression Grammar) 来打算自己写 parser,一个简单的模型:

    LineBreak <- "\r\n" / "\n" / "r"
    Char <- !LineBreak .
    Comment <- "//" Char* LineBreak / "/*" (!"*/" Char)* "*/"

    左边的是 expression
    . 代表任意字符,* 代表可以有零个或者多个前面的 expr,! 是一个 not predicate,如果后面的 expr 匹配成功则失败,失败则成功,但是它不会消耗字符。/ 代表如果前面一个失败了,则选择后面的,即所谓的 prioritized choose。

    语文能力拙计,所以描述的大家看不懂请见谅啊……有兴趣可以去看这篇论文: http://pdos.csail.mit.edu/~baford/packrat/popl04/peg-popl04.pdf

    然后回到我这个问题。因为我论文没看太懂(后面的数学证明太可怕,我毕竟还年轻),于是打算使用一种比较直观的方式,用 Javascript 动手实现一遍,我是这样定义的。

    function (input, index) -> { output, index } / failed

    满足这样的函数都被称为 expression,input 是给的字符串,index 代表从哪儿开始 consume,然后返回的 output 是结果,index 是指新的 consume starting position。如果失败了,就返回一个对象 failed,其实就是 var failed = {},Javascript 对象都是指针,我没有理解错吧。

    然后我要实现一下上面的这些 not predicate 啊、prioritized choose 啊什么的,就是把 expr(现有函数)给这些高阶函数(是这么称呼吧),返回新的 expr,这样写起来比较直观好玩,当然 stack size 太恐怖,生产环境肯定不会这么用。可能是我最近看 SICP 中毒了吧。

    比如说上面的 parse C++ 注释的,就可以这么写:

    var LineBreak = PEG['/'](PEG['t']('\r\n'), PEG['t']('\n'), PEG['t']('\r'));

    PEG['t'] 接受一个字符串,返回能 consume 这个字符串的 expr。

    var Char = PEG['s'](PEG['!'](LineBreak), PEG['.']);

    PEG['s'] 就是把几个 expr 连在一起,返回值用数组装着,一个失败了,就全部失败。

    var Commnet = PEG['/'](PEG['s'](PEG['t']('//'), PEG['*'](Char), LineBreak), PEG['s'](PEG['t']('/*'), balabala, PEG['t']('*/')));

    好吧我知道不能直接写 // 但是为了省事大家就不要在意了。

    于是乎,回到我的问题,大家知道很多 expr 需要递归:

    CppComment <- // Char* LineBreak
    CCommentHelper "*/" / Char CComentHelper
    CComment <= "/*" CCommentHelper

    var CCommentHelper = ......

    在这种情况下,我的那些高阶函数需要操作一个暂时还不存在的函数,如何是好?虽然可以用七楼的办法,但是那样一点都不优雅……实际上我已经放弃自己写 PEG parser 的打算了,只是保留了一份好奇,有没有办法绕过这个问题?^_^
    14 条回复    1970-01-01 08:00:00 +08:00
    rannnn
        1
    rannnn  
       2014-04-19 12:17:21 +08:00
    没懂
    mikawudi
        2
    mikawudi  
       2014-04-19 12:43:50 +08:00 via Android
    参数先被求值,是指应用序和正则序的问题么?
    jakwings
        3
    jakwings  
       2014-04-19 13:22:38 +08:00
    那个 BIContent 的定义简直是乱来。从纯逻辑上判断是根本不可能存在的。去掉那些杂碎后就好像在说:

    > y = y;
    < ReferenceError: y is not defined

    简直无语。当然,用 var x = x; 是不会有错误提示的,因为有语法糖,相当于 var x = undefined; x = x;
    jakwings
        4
    jakwings  
       2014-04-19 13:27:33 +08:00
    @jakwings Javascript 里面没有 C 语言那样的指针,除非你把 BIContent 赋到某个对象上,参数传的那个对象,还可以临时生成一个函数放上去,否则就别想无中生有了。
    notcome
        5
    notcome  
    OP
       2014-04-19 15:38:33 +08:00
    @mikawudi 是的,我希望生成一个函数调用自身
    notcome
        6
    notcome  
    OP
       2014-04-19 16:05:06 +08:00
    似乎想到一个好的办法

    var self = {value: 'self'};
    var failed = {value: 'failed'};

    function prioritized_choose (choices) {
    function this_expr (input, index) {
    var result = failed;
    for (var i = 0; i < choices.length; i ++) {
    var test;
    if (choices[i] == self)
    test = this_expr(input, index);
    else
    test = choices[i](input, index);
    if (test != failed) {
    result = test;
    break;
    }
    }
    return result;
    }
    }

    嗯, 还没测试, 有语法错误就逗比了……
    但问题是需要调用这个 prioritized_choose(choices) 的是 choices 里的一个 function, 所以这个方法对我来说毫无价值, 呜呜. 求大神支援.
    notcome
        7
    notcome  
    OP
       2014-04-19 16:09:00 +08:00
    哦对可以新建一个对象:
    var func_obj = {};
    func_obj.func = xxxx(xxxx, func_obj);
    func_obj.func(xxxx);

    感觉好不优美……
    xgod
        8
    xgod  
       2014-04-19 19:15:02 +08:00
    function BIGen (token) {
    var BICOntent= R['/']([token, R['s']([Char, BIContent])]);
    return R['s']([token, Char, BIContent]);
    }

    function series (first, second) {
    return function (input, index) {
    var result = first(input, index);
    if (result) return result;
    result = second(input, index);
    return {output: result.output, index: result.index};
    }
    }

    ------------
    真心不明白你想描述的问题是什么。你想返回一个闭包,然后在闭包函数里面引用闭包函数本身?
    是这意思?
    function series (first, second) {
    var nOname= function (input, index) {
    var result = first(input, index);
    if (result) return result;
    result = noname(input, index);
    return {output: result.output, index: result.index};
    }
    return noname;
    }
    xgod
        9
    xgod  
       2014-04-19 19:18:23 +08:00
    如果没有明确的思路,不建义把代码写得这么绕,估计你想要的是一种类似延时执行的代码绑定操作。
    如果是解析个语法解析器,写一个状态机模式,想好状态变化,写出来的性能与可读性也不会差。
    jakwings
        10
    jakwings  
       2014-04-19 19:44:44 +08:00
    假如你的想法就是 7 楼那样的话,下面的实现够了:
    https://gist.github.com/anonymous/a9aac2e6f96b53f1f1ac
    Golevka
        11
    Golevka  
       2014-04-20 01:49:50 +08:00
    我感觉只要按照传统的stream的方法做就行了。你之所以会感觉难受是因为stream函数的类型是没办法直接写出来的
    bolasblack
        12
    bolasblack  
       2014-04-22 06:56:37 +08:00
    @notcome 实话说那段代码没看懂,不过如果只是要函数自己要调用自己的话,其实是可以使用 arguments.callee 这个东西的
    easing
        13
    easing  
       2014-04-22 12:47:42 +08:00   1
    你要做的貌似是一个简化版的parser combinator, 但你这composite函数的方式不太对。Parser和做parse的函数应该分开构建,你用一个高阶函数做参数本身来定义这个高阶。。。
    notcome
        14
    notcome  
    OP
       2014-04-22 16:36:44 +08:00
    @easing 虽然不太懂您说的分开是指什么……但是没关系我照着 parser combinator 搜就可以了,学习中,谢谢!
    关于     帮助文档     自助推广系统/a>     博客     API     FAQ     Solana     5443 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 07:30 PVG 15:30 LAX 00:30 JFK 03:30
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86