電卓アプリをFRP的に実装するには?

投稿者: Anonymous

数値入力イベントのstreamと
-1-2-3---4-5-6---7-8-9--

演算子入力イベントのstreamから、
-------+-------+--------

計算に用いるオペランドのstreamを生成して、
---123-----456-----789--

最終的に演算結果のstreamを生成する
---123-----579-----1368-

という流れかなと考えているんですが、実装方法がイマイチつかめていません。

試行錯誤をしてみた結果が以下ですが、結局scanで無理やり実装している感じです。

_x000D_

_x000D_

var ds = $('.digit').asEventStream('click').map(function(d) {_x000D_
  return parseInt(d.target.textContent)_x000D_
});_x000D_
var os = $('.operation').asEventStream("click").map('.target.textContent');_x000D_
_x000D_
var buffer = ds.merge(os).scan({_x000D_
  current: 0,_x000D_
  result: 0,_x000D_
  display: 0,_x000D_
  nextOperation: '+'_x000D_
}, function(buf, v) {_x000D_
  if (isFinite(v)) {_x000D_
    buf.current = buf.current * 10 + v;_x000D_
    buf.display = buf.current;_x000D_
  } else {_x000D_
    buf.result = eval('buf.result ' + buf.nextOperation + ' buf.current');_x000D_
    buf.result = isFinite(buf.result) ? buf.result : 0;_x000D_
    buf.display = buf.result;_x000D_
    buf.current = 0;_x000D_
    buf.nextOperation = v;_x000D_
  }_x000D_
  return buf;_x000D_
});_x000D_
_x000D_
buffer.onValue(console, 'log');_x000D_
buffer.map('.display').onValue($('#display'), 'text');

_x000D_

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>_x000D_
<div>_x000D_
  <button class="digit">1</button>_x000D_
  <button class="digit">2</button>_x000D_
  <button class="digit">3</button>_x000D_
  <button class="digit">4</button>_x000D_
  <button class="digit">5</button>_x000D_
  <button class="digit">6</button>_x000D_
  <button class="digit">7</button>_x000D_
  <button class="digit">8</button>_x000D_
  <button class="digit">9</button>_x000D_
  <button class="digit">0</button>_x000D_
  <button class="operation">+</button>_x000D_
  <button class="operation">-</button>_x000D_
  <button class="operation">*</button>_x000D_
  <button class="operation">/</button>_x000D_
</div>_x000D_
<label id="display"></label>

_x000D_

_x000D_

_x000D_

FRP的には本来どのように実装するのでしょうか?

解決

FRP ということではなく、 Bacon.js 限定ですが、ストリームの出力が異なるので、 Bacon.update でまとめるとすっきりします。 sampledBy が使えるかと思いましたが、どこに蓄積される情報を置くかが難しいですね。

_x000D_

_x000D_

function newData(result, curr, op) {_x000D_
  return { result: result, curr: curr, op: op };_x000D_
}_x000D_
_x000D_
function accumNum(orig_num, num) {_x000D_
  return orig_num * 10 + num;_x000D_
}_x000D_
_x000D_
// .digit -> number_x000D_
var digit = $('.digit').asEventStream('click')_x000D_
  .map(function (ev) { return parseInt(ev.target.textContent); });_x000D_
_x000D_
// .operation -> function(a, b)_x000D_
var operation = $('.operation').asEventStream("click")_x000D_
  .map(function(e) {_x000D_
    return function(a, b) {_x000D_
      return b != null ? eval('a' + e.target.textContent + "b") : a;_x000D_
    };_x000D_
  });_x000D_
_x000D_
// .reset -> tick_x000D_
var reset = $('.reset').asEventStream("click");_x000D_
_x000D_
Bacon.update(newData(0, null, accumNum), _x000D_
  [digit], function(data, value) {_x000D_
    return newData(data.result, accumNum(data.curr, value), data.op);_x000D_
  }, [operation], function(data, f_ab) {_x000D_
    return newData(data.op(data.result, data.curr), null, f_ab);_x000D_
  }, [reset], function(data, unused) {_x000D_
    return newData(0, null, accumNum);_x000D_
  }_x000D_
).onValue(function(v) {_x000D_
  console.log(v); $('#display').text(v.result);_x000D_
});

_x000D_

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/bacon.js/0.7.47/Bacon.min.js"></script>_x000D_
_x000D_
Calculator (bacon.js) <hr/>_x000D_
<div>_x000D_
    <button class="digit">1</button>_x000D_
    <button class="digit">2</button>_x000D_
    <button class="digit">3</button>_x000D_
    <button class="digit">4</button>_x000D_
    <button class="digit">5</button>_x000D_
    <button class="digit">6</button>_x000D_
    <button class="digit">7</button>_x000D_
    <button class="digit">8</button>_x000D_
    <button class="digit">9</button>_x000D_
    <button class="digit">0</button>_x000D_
</div><hr/> _x000D_
<div>_x000D_
    <button class="operation">+</button>_x000D_
    <button class="operation">-</button>_x000D_
    <button class="operation">*</button>_x000D_
    <button class="operation">/</button>_x000D_
    <button class="reset">CLR</button>_x000D_
</div><hr/>_x000D_
<label id="display"></label>

_x000D_

_x000D_

_x000D_

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *