2017年5月4日木曜日

関数型プログラミングの基礎 継続を使った非決定計算機

継続について書いてあるが、かなりこみ入っている。
とりあえず、以下のようにしたら、動作させることはできた。
おおまかな流れを把握するので精一杯だった。
式の評価とか、なんらかの構造をもつものについてのアルゴリズムは、複雑になりやすいので、こういった方法で少しでも、整理してプログラミングをしていくということなのでしょう。。。

①add(amb(リスト1),amb(リスト2))がdirverの中のcalculateの第1引数へ
②calculateの中のmatchにより、add:(x,y)にマッチ
③その中で再帰でcalculateの第1引数へ amb(リスト)の形なので
④amb:(choice)にmatch また再帰でcalculateの第1引数へ headであるnum()の形で
⑤最終的には num:(n)にmatch return contiuesOnSuccess(n,contiunesOnFailure);
に到達する。 contiuesOnSuccessは第1引数anyvalue(今の場合n)をそのまま返している。suspendedComputationにはfailureのほうにある残りのリストが保持されることになるらしい。

代数的データ構造のパターンマッチについては、以前でてきた方法
      var exp = {
        match : (anExp, pattern) => {
          return anExp.call(exp, pattern);
        },
     amb : (alist) => {
          return (pattern) => {
            return pattern.amb(alist);
          };
        },
        num : (n) => {
          return (pattern) => {
            return pattern.num(n);
          };
        },
        add : (exp1, exp2) => {
          return (pattern) => {
            return pattern.add(exp1, exp2);
          };
        }
      };


評価関数
      var calculate = (anExp,  continuesOnSuccess, continuesOnFailure) => {
                         return exp.match(anExp, {
 ※数値の場合           num: (n) => {
                             return continuesOnSuccess(n, continuesOnFailure);
                           },
 ※足し算の場合       add: (x, y) => {
                             return calculate(x, (resultX, continuesOnFailureX) => {
                               return calculate(y, (resultY, continuesOnFailureY) => {
 引数xとyがともに成功すれば、両者の値で足し算を計算
                                 return continuesOnSuccess(resultX + resultY, continuesOnFailureY);
                               }, continuesOnFailureX);
  y の計算に失敗すれば、xの失敗継続を渡す
                             }, continuesOnFailure);
  x の計算に失敗すれば、おおもとの失敗継続を渡す
                           },
 ※amb式の場合     amb: (choices) => {
                              var calculateAmb = (choices) => {
                               return list.match(choices, {
                                 empty: () => {      
                                   return continuesOnFailure();
                                 },
                                 cons: (head, tail) => {
                                   return calculate(head, continuesOnSuccess, (_) => {
 失敗継続で後尾を計算
                                     return calculateAmb(tail);
                                   });
                                 }
                               });
                             };
                             return calculateAmb(choices);
                           }
                         });
                       };

非決定計算機の駆動関数
      var driver = (expression) =>{ 
        var suspendedComputation = null;//中断された計算を継続として保存する変数
 成功継続
        var continuesOnSuccess = (anyValue,
                                  continuesOnFailure) => {//再開に備えて、失敗継続を保存
                                    suspendedComputation = continuesOnFailure;
                                    return anyValue;
                                  };
 失敗継続
        var continuesOnFailure = () => {
          return null;
        };
 内部に可変な状態suspendedComputationを持つクロージャーを返す
        return () => { //中断された継続がなければ、最初から計算
          if(suspendedComputation === null) {
            return calculate(expression, continuesOnSuccess,  continuesOnFailure);
          } else { //中断された継続があれば、その継続を実行
            return suspendedComputation();
          }
        };
      };


リストモジュールも必要
  // expect(array).to.an('array');はコメントアウトしないとだめだった。

テスト
var ambExp = exp.add(
          exp.amb(list.fromArray([exp.num(1),exp.num(2)])),
          exp.amb(list.fromArray([exp.num(3),exp.num(4)])));
var calculator = driver(ambExp);


  calculator() ;
  calculator() ;
  calculator() ;
  calculator() ;



0 件のコメント:

コメントを投稿