livesense tech night immutable-js at a glance

Post on 17-Jul-2015

585 Views

Category:

Engineering

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Immutable @ JavaScript

var  me  =  {      name:      "Yuta  Shimakawa",      tw:          "@banana_umai",      qiita:    "bananaumai",      github:  "bananaumai"  };

ではございません

at a glance...

不変データ

Seq List Map

OrderedMap Set

OrderedSet Record

var  list1  =  Immutable.List.of(1,  2);  var  list2  =  list1.push(3,  4,  5);  var  list3  =  list2.unshift(0);  var  list4  =  list1.concat(list2,  list3);  assert(list1.size  ===  2);  assert(list2.size  ===  5);  assert(list3.size  ===  6);  assert(list4.size  ===  13);  assert(list4.get(0)  ===  1);  

https://github.com/facebook/immutable-js/

JSオブジェクトとの 相互変換

var  map1  =  Immutable.Map({a:1,  b:2,  c:3,  d:4});  var  map2  =  Immutable.Map({c:10,  a:20,  t:30});  var  obj  =  {d:100,  o:200,  g:300};  var  map3  =  map1.merge(map2,  obj);  //  Map  {  a:  20,  b:  2,  c:  10,  d:  100,  t:  30,  o:  200,  g:  300  }

https://github.com/facebook/immutable-js/

var  deep  = Immutable.Map(  {  a:  1,  b:  2,  c:  Immutable.List.of(3,  4,  5)  });  

deep.toObject()  //  {  a:  1,  b:  2,  c:  List  [  3,  4,  5  ]  }  deep.toArray()  //  [  1,  2,  List  [  3,  4,  5  ]  ]  deep.toJS()  //  {  a:  1,  b:  2,  c:  [  3,  4,  5  ]  }  JSON.stringify(deep)  //  '{"a":1,"b":2,"c":[3,4,5]}'

入れ子データ

var  nested  =  Immutable.fromJS({a:{b:{c:[3,4,5]}}});  //  Map  {  a:  Map  {  b:  Map  {  c:  List  [  3,  4,  5  ]  }  }  }  

var  nested2  =  nested.mergeDeep({a:{b:{d:6}}});  //  Map  {  a:  Map  {  b:  Map  {  c:  List  [  3,  4,  5  ],  d:  6  }  }  }  

var  nested3  =  nested2.updateIn(['a',  'b',  'd'],  value  =>  value  +  1);  

//  Map  {  a:  Map  {  b:  Map  {  c:  List  [  3,  4,  5  ],  d:  7  }  }  }  

var  nested4    =  nested3.updateIn(['a',  'b',  'c'],  list  =>  list.push(6));  

//  Map  {  a:  Map  {  b:  Map  {  c:  List  [  3,  4,  5,  6  ],  d:  7  }  }  }

https://github.com/facebook/immutable-js/

遅延評価(Seq)

var  oddSquares  =  Immutable.Seq.of(1,2,3,4,5,6,7,8)                                      .filter(x  =>  x  %  2)                                      .map(x  =>  x  *  x);  //  not  computed  here!      oddSquares.get(1);  //  9

https://github.com/facebook/immutable-js/

同等性

var  map1  =  Immutable.Map({a:1,  b:1,  c:1});  var  map2  =  Immutable.Map({a:1,  b:1,  c:1});  assert(map1  !==  map2);  assert(Immutable.is(map1,  map2)  ===  true);

https://github.com/facebook/immutable-js/

JSで不変データ

Immutableなリストを 素朴に実装してみた

cloneによる実装

var  _  =  require('lodash');      function  iPush(arr,  val)  {      var  arr2  =  _.clone(arr);      arr2.push(val);      return  arr2;  }      var  arr  =  [];  for  (var  i  =  0;  i  <  100000;  i++)  {      arr  =  iPush(arr,  i);  }

https://gist.github.com/bananaumai/cc2f4d90662aa823ce9e

…は cloneのコストが膨大

Linked Listを実装

var  LinkedList  =  {};  LinkedList.prototype  =  {      head:  function()  {          return  this._head;      },          tail:  function()  {          return  this._tail;      },          push:  function(val)  {          return  new  NonEmptyList(val,  this);      },          forEach:  function(fnc)  {          fnc(this._head);          if  (this._tail  instanceof  NonEmptyList)  {              this._tail.forEach(fnc);          }      }  };  

var  EmptyList  =  function()  {      this._head  =  null;      this._tail  =  null;  };  EmptyList.prototype  =  Object.create(LinkedList.prototype);  EmptyList.prototype.constructor  =  EmptyList;

https://gist.github.com/bananaumai/164b24b264e0f917007c

var  NonEmptyList  =  function(head,  tail)  {      this._head  =  head;      this._tail  =  tail;  };  NonEmptyList.prototype  =  Object.create(LinkedList.prototype);  NonEmptyList.prototype.constructor  =  NonEmptyList;    

LinkedList.of  =  function()  {      var  _create  =  function(vals,  list)  {          if  (vals.length  ===  0)  return  list;          var  head  =  vals.shift();          return  _create(vals,  new  NonEmptyList(head,  list))      };          return  _create(          Array.prototype.slice.call(arguments).reverse(),          new  EmptyList()      );  };

var  list  =  LinkedList.of();  for  (var  i  =  0;  i  <  100000;  i++)  {      list  =  list.push(i);  }  

https://gist.github.com/bananaumai/164b24b264e0f917007c

…は (&pushはそれなりに速いけど・・・)

末尾再帰最適化 されないので難しい (&Object.ArrayのAPIの実現しようとすると・・・)

immutable-jsのアプローチ

Trie

http://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%82%A4%E6%9C%A8

(def  list  [0  1  2  3  4  5])

ClojureやScalaが採用 しているアプローチ

Immutable.Listの実装

文字列のキーの代わりに 添字の数値をbitに変換、パーティショニング

var  list  =  Immutable.List.of(1,…,1000);  list.get(887);

http://hypirion.com/musings/understanding-persistent-vector-pt-2#f1n

実際は5bitずつ分割するので ↓

O(Log32N)

実際は32通り

http://hypirion.com/musings/understanding-persistent-vector-pt-1

var  list1  =  Immutable.List.of(1,2,3,4,5,6,7,8);  var  list2  =  list1.set(5,  "beef");

list1 list2

http://hypirion.com/musings/understanding-persistent-vector-pt-1

要素のpushvar  list1  =  Immutable.List.of(…);  var  list2  =  list.push(…);

最右のリーフノードに空きがある場合

http://hypirion.com/musings/understanding-persistent-vector-pt-1

最右のリーフノードに空きがない場合

http://hypirion.com/musings/understanding-persistent-vector-pt-1

rootから伸びるすべてのリーフが埋まっている場合

http://hypirion.com/musings/understanding-persistent-vector-pt-1

要素のpopvar  list1  =  Immutable.List.of(…);  var  list2  =  list.pop();

最右のリーフノードが2つ以上の要素を持つ場合

http://hypirion.com/musings/understanding-persistent-vector-pt-1

最右のリーフノードが1つの要素を持つ場合

http://hypirion.com/musings/understanding-persistent-vector-pt-1

削除後rootノードが1つの要素だけをもつ場合

http://hypirion.com/musings/understanding-persistent-vector-pt-1

簡単なまとめ

Trieによるリストの表現 O(Log32N)のアクセス 最小限のコピー

top related