Warp9: reactive primitives

Post on 13-Jul-2015

16,556 views 0 download

Tags:

transcript

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Реактивные примитивы Warp9

@rystsov

2013

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Warp9 поддерживает реактивные переменные (Cell) иреактивный списки (List).

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell: реактивные переменные

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / ctor

Реактивная переменная в warp9 может содержать какое-либозначение или быть пустой, по умолчанию переменная

создается пустой.

var a = new Ce l l ( ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / set

Для того чтобы положить в неё какое-либо значениеиспользуется метод “set”

var a = new Ce l l ( ) ;a . s e t ( 4 2 ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / ctor(…)

Альтернатива - передать начальное значение в конструкторе

var a = new Ce l l ( 4 2 ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / unset

В любой момент переменную можно сделать пустой

var a = new Ce l l ( 4 2 ) ;a . unse t ( ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / unwrap

У переменной можно вызвать метод unwrap для того, чтобыполучить значение, которое она содержит

var a = new Ce l l ( 4 2 ) ;c on so l e . i n f o ( a . unwrap ( ) ) ;//> 42

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / unwrap(…)

Если переменная была пустой - вылетит исключение, ноunwrap можно передать значение, которое следует вернуть,

если переменная пустая

con so l e . i n f o (new Ce l l ( ) . unwrap ( 4 2 ) ) ;//> 42

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / li

К существующей переменной можно применить функцию иполучить реактивную переменную, которая связана с первой

этой функцией: при изменении первой - автоматическименяется вторая

var a = new Ce l l ( ) ; // a is unsetvar b = a . l i f t ( f unc t i on ( a ) {

re turn a+2 ;} ) ; // b is unseta . s e t ( 1 ) ; // a contains 1, b contains 3a . s e t ( 5 ) ; // a contains 1, b contains 7a . unse t ( ) ; // a is unset , b is unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / li

Можно сказать, что для метода li выполняетсяследующий закон

f o r a l l ( f , x ) :new Ce l l ( x ) . l i f t ( f ) . unwrap ( ) == f ( x ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / li

У переменных полученных лифтингом (а врочем и любымдругим способом, кроме как вызовом конструктора Cell)

невозможно вызывать методы set, unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / coalesce

Еще один способо создать переменную - вызвать метод coalesceи передать ему значение по умолчанию. Если исходнаяпеременная содержит какое-либо значение, то и новая

переменная будет его содержать, если исходная переменнаяпустая, то новая переменная будет содержать значение по

умолчанию.

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / coalesce

var a = new Ce l l ( ) ;var b = a . c o a l e s c e ( 4 2 ) ; // b contains 42a . s e t ( 1 3 ) ; // a contains 13, b contains 13a . unse t ( ) ; // a is unset , b contains 42

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / isSet

Вызов isSet вернет реактивную переменную, которая содержитtrue если исходная не пустая, и false в ином случае.

var a = new Ce l l ( ) ;var b = a . i s S e t ( ) ; // b has falsea . s e t ( 1 3 ) ; // a has 13, b has truea . unse t ( ) ; // a is unset , b has false

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…)

Другой способ создать переменную - вызвать метод when. Еслиметод вызван с одним параметром, то параметр

рассматривается как фильтр. Если значение фильтра true, топеременная будет содержать значение, совпадающее с

исходной, иначе переменная будет пустая.

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…)

var a = new Ce l l ( ) ;var b = a . when ( f unc t i on ( a ) {

re turn a>3 ;} ) ; // b in unseta . s e t ( 4 2 ) ; // a contains 42, b contains 42a . s e t ( 4 ) ; // a contains 4, b contains 4a . s e t ( 1 ) ; // a contains 1, b is unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when

Если в качестве фильтра передать не функцию, то значениеисходной переменной будет сравниваться с “фильтром” на

равенство.

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …)

Так же методу when можно передать два параметра: фильтр итрансформер, в это случае трансформер будет применен кзначению исходной переменной, если фильтр вернул true.

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …)

var a = new Ce l l ( ) ;var b = a . when (

f unc t i on ( a ) { re turn a>3 ; } ,f unc t i on ( x ) { re turn x+1 }

) ; // b in unseta . s e t ( 4 2 ) ; // a contains 42, b contains 43a . s e t ( 4 ) ; // a contains 4, b contains 5a . s e t ( 1 ) ; // a contains 1, b is unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …)

На самом деле when с двумя аргументами практическиэквивалентен комбинации when и li

f o r a l l ( f , t , c e l l ) :c e l l . when ( f , t ) == c e l l . when ( f ) . l i f t ( t ) ;

Практически, потому что, как и в случае с одним параметром -вместо фильтра и трансформера можно передать константы -

“фильтр’’ будет сравниваться с значением исходнойпеременной, а “трансформер’’ вернет себя

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …)

var a = new Ce l l ( ) ;var b = a . when ( 4 2 , 1 3 ) ; // b in unseta . s e t ( 4 2 ) ; // a contains 42, b contains 13a . s e t ( 4 ) ; // a contains 4, b is unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …, …)

Последняя форма when - три аргумента: фильтр, трансформери алтернативный трансформер, работает точно так же как ипредыдущий, но если фильтр вернул false - применяется

алтернативный трансформер

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …, …)

var a = new Ce l l ( ) ;var b = a . when (

f unc t i on ( a ) { re turn a>=0 ; } ,f unc t i on ( x ) { re turn x+1 } ,f unc t i on ( x ) { re turn x−1 ; }

) ; // b in unseta . s e t ( 0 ) ; // a contains 0, b contains 1a . s e t (−1 ) ; // a contains -1, b contains -2a . unse t ( ) ; // a is unset , b in unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …, …)

Вместо функций можно передать значения, в этом случае whenведет себя как реактивный тернарный оператор

var a = new Ce l l ( ) ;var b = a . when ( true , 1 , 0 ) ; // b is unseta . s e t ( t rue ) ; // a has true, b has 1a . s e t ( f a l s e ) ; // a has false , b has 0a . unse t ( ) ; // a is unset , b is unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …, …)

За ислючения случая с константами, для when с тремяаргументами выполняется закон

f o r a l l ( c , f , t , a ) :c . when ( f , t , a ) ==c . l i f t ( f unc t i on ( c ) {

re turn f ( c ) ? t ( c ) : a ( c )} ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind

Теперь рассмотрим последний метод Cell для создания новыхпеременных - bind. Если бы мне пришлось выбирать из li,

when и bind - я бы выбрал bind, так как остальные легко из неговыводятся

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind

Bind’у на вход нужно передавать функцию. Эта функцияприменяется к значению реактивной переменной и

возвращает Cell, в свою очередь bind возвращает новуюреактивную переменную, которая содержит то же значение,

что и переменная, которую вернула функция. Звучит cтрашно,но надеюсь после примеров станет понятнее, но вначале…

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind

…закон

f o r a l l ( c e l l , f ) :c e l l . b ind ( f ) . unwrap ( )

==f ( c e l l . unwrap ( ) ) . unwrap ( )

Сложно сказать зачем именно нужен этот монстр, так какприменяется он практически везде, но начнем по порядку, я

сказал, что с него помощью можно создать li…

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind / li

var c e l l = new Ce l l ( ) ;c e l l . l i f t = f unc t i on ( f ) {

re turn t h i s . b ind ( f unc t i on ( x ) {re turn new Ce l l ( f ( x ) ) ;

} ) ;}

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind / when

Теперь реализуем самую первую форму when

var c e l l = new Ce l l ( ) ;c e l l . when = f unc t i on ( f ) {

re turn t h i s . b ind ( f unc t i on ( x ) {re turn f ( x ) ? new Ce l l ( x ) : new

Ce l l ( ) ;} ) ;

} ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind / binary

Это далеко не все, например, с помощью bind можно взятьлюбую бинарную функцию от обычных значений и

возвращающую обычное значение, и применить её к двумреактивным переменным и получить реактивную переменную

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind / binary

var a = new Ce l l ( 1 ) ;var b = new Ce l l ( 2 ) ;var sum = a . b ind ( f unc t i on ( a ) {

re turn b . b ind ( f unc t i on ( b ) {re turn new Ce l l ( a+b ) ;

} ) ;} ) ; // sum has 3a . s e t ( 2 ) ; // a has 2, b has 2, sum has 4b . unse t ( ) ; // a has 2, b & sum are unset

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Монады

Как только вы скажете “Ага, я все понял!” - поздравляю, вытолько что познакомились с монадами

(черт, я же не хотел писать очередной туториал по монадам)

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Переходим к реактивным спискам (List)

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / ctor

Создаем список

var l i s t = new L i s t ( ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / ctor(…)

Списку можно передать начальные значения в конструкторе.

var l i s t = new L i s t ( [ 1 , 2 , 3 ] )

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / add

Кладем элементы в список

var l i s t = new L i s t ( ) ;l i s t . add ( "Warp9" ) ;l i s t . add ( "React" ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / unwrap

Получить содержимое списка можно через unwrap

var l i s t = new L i s t ( ) ;l i s t . add ( "Warp9" ) ;c on so l e . i n f o ( l i s t . unwrap ( ) ) ;// ["Warp9"]

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / remove

Метод add возвращает id элемента,по которому его можно удалить

var l i s t = new L i s t ( ) ;var warpId = l i s t . add ( "Warp9" ) ;var r e a c t I d = l i s t . add ( "React" ) ;c on so l e . i n f o ( l i s t . unwrap ( ) ) ;//> ["Warp9", "React"]l i s t . remove ( r e a c t I d ) ;c on so l e . i n f o ( l i s t . unwrap ( ) ) ;//> ["Warp9"]

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / add(λ)

Часто id элемента должен содержаться внутри элемента,поэтому методу add можно передать функцию, эта функция

будет вызвана, аргументом будет id, а в список будет добавленрезультат выполнения этой функции

var l i s t = new L i s t ( ) ;l i s t . add ( f unc t i on ( i d ) {

re turn { i d : id , name : "Warp" } ;} ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / removeWhich

Кроме remove, есть еще один метод - removeWhich, онпринимает предикат и удаляет из списка все элементы,

которые удоволетворяют этому предикату

var l i s t = new L i s t ( [ 1 , 2 , 3 ] ) ;l i s t . removeWhich ( f unc t i on ( x ) {

re turn x < 2 ;} ) ;// list contains 2,3

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / forEach

Так же в списке есть метод forEach, который ведет себя подобноforEach массива

var l i s t = new L i s t ( ["Warp9" , "React"

] ) ;l i s t . f o rEach ( f unc t i on ( x ) {

c on so l e . i n f o ( x ) ;} ) ;//> Warp9//> React

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / remove, removeWhich и forEach

Методы remove, removeWhich и forEach не учитываютреактивную природу списка и выполняются один раз в момент

вызова (не перевыполняются при добавлении новыхэлементов)

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / li

Так же как и Cell, в List есть метод li, который создает новыйсписок и связывает его функцией с исходным, получается

реактивный map

var a = new L i s t ( ) ;var b = a . l i f t ( f unc t i on ( x ) { re turn x+2 ; } ) ;// b.unwrap()==[]var i d 1 = a . add ( 1 ) ;// a.unwrap()==[1], b.unwrap()==[3]var i d 2 = a . add ( 2 ) ;// a.unwrap()==[1,2], b.unwrap()==[3,4]a . remove ( i d 1 ) ;// a.unwrap()==[2], b.unwrap()==[4]

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / liing

У списка полученный через лифтинг невозможно вызватьметоды add, remove, removeWhich

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce

Все, что я пока рассказал про списки есть и в Knockout иReactiveCoffee, но я обещал, что warp9 поддерживает больше, а

именно агрегацию. Начнем с метода reduce и попробуемпросуммировать элементы в списке и получить реактивное

значение суммы

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / sum

Первый параметр reduce - значение, которое соответствуетпустому списку, второе значение - функция, которая

занимается сверткой

var l i s t = new L i s t ( ) ;var sum = l i s t . r educe ( 0 , f unc t i on ( a , b ) {

re turn a+b ;} ) ;l i s t . add ( 4 1 ) ; // sum has 41l i s t . add ( 1 ) ; // sum has 42

Как мы видим переменная sum получилась реактивной иизменяется при изменении списка.

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / count

После того, как мы посчитали сумму списка, давайте узнаемкол-во элементов в нем

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / count

var l i s t = new L i s t ( ) ;var count = l i s t . l i f t ( f unc t i on ( x ) {

re turn 1 ;} ) . r educe ( 0 , f unc t i on ( a , b ) {

re turn a+b ;} ) ;var i d 4 1 = l i s t . add ( 4 1 ) ; // count has 1l i s t . add ( 1 ) ; // count has 2l i s t . remove ( i d 41 ) ; // count has 1

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / count / wrap

Это работающий метод, правда требует создания новогосписка; существует чуть более эффективный путь

var count = l i s t . r educe ( 0 , f unc t i on ( a , b ) {re turn a+b ;

} , {wrap : f unc t i on ( x ) {

re turn 1 ;}

} ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / wrap

Функция wrap выполняется для каждого элемента списка и ужееё результат агрегируется, очевидно, что выполняется закон

f o r a l l ( l i s t , f , id , f o l d ) :l i s t . r educe ( id , f o l d , { wrap : f } )

==l i s t . l i f t ( f ) . r educe ( id , f o l d )

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / эффективность

Раз мы заговорили об эффективности, какова сложноть уфункции reduce, при добавлении элемента в список размера n?В knockout она равна O(n), в warp9 она O(lnn). Но делать даже

lnn вычислений на каждое добавление элемента в список,чтобы инкременировать одно значение (sum или count)

странно, поэтому в warp9 есть другой путь

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduceMonoid

Если посмотреть на реализацию функции reduce, видно, чтовызов list.reduce(id, fold) раскрывается в

l i s t . reduceMonoid ( {i d e n t i t y : id ,add : f o l d

} ) ;

Искушенный читатель догадается, что раз есть Monoid, то естьи Group, и будет прав

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduceGroup

Пример с суммой элементов списка:

var sum = l i s t . r educe ( 0 , f unc t i on ( a , b ) {re turn a+b ;

} ) ;

можно переписать так:

var sum = l i s t . reduceGroup ( {i d e n t i t y : f unc t i on ( ) { re turn 0 } ,add : f unc t i on ( a , b ) { re turn a+b ; } ,i n v e r t : f unc t i on ( x ) { re turn −x ; }

} ) ;

И сложность, магическим образом, упадет с O(lnn) до O(1).Аналогично можно посчитать и кол-во элементов в списке

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce∗

Понять, можно ли использоват reduce∗ и какой вариантиспользовать достаточно просто. Если агрегат списка не

зависит от порядка элементов в списке и по посчитанномуагрегату и вставляемому элементу вы можете посчитать

значение нового агрегата, то агрегат можно выразить черезreduce∗ функции.

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduceGroup

Если зная агрегат и удаляемый элемент вы сможете посчитатьзначение, которое совпадет с агрегатом нового списка, то

агрегат можно выразить через reduceGroup

Очевидно, что агрегаты sum и count попадают под обаопределения, а значит мы можем для них использовать

reduceGroup

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduceMonoid

Может сложится впечатление, что любой коммутативныйагрегат попадает под это определение,

но на самом деле это не так.

Допустим, мы хотим посчитать логическое & от списка bool.Данная задача попадает под reduceMonoid , но не под

reduceGroup

Зная, что агрегат false и зная, что удаляемый элемент тожеfalse, мы не обладаем информацией, есть ли в списке еще false,

а от этого зависит будет ли агрегат false или true

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce(&)

Хотя я вру

На самом деле, задачу логического & можно свести кreduceGroup. Достаточно каждое true рассматривать как пару

(1,1), каждое false как (0,1), при добавлении элемента -покомпонентно складывать пары, при удалении - удалять.

Если в каждой компоненте одинаковое число - значитзначение агрегата true, иначе false. Единственная проблема - в

качестве результата будет пара, а не boolean

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / unwrap

Оказывается в warp9 это не проблема, так как есть опцияunwrap

После того как reduce отработает, вызывается unwrap ипреобразовывает результат в нужный тип..

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / unwrap

l i s t . reduceGroup ( {i d e n t i t y : f unc t i on ( ) { re turn [ 0 , 0 ] ; } ,add : f unc t i on ( x , y ) {

re turn [ x [ 0 ]+y [ 0 ] , x [ 1 ]+y [ 1 ] ] ;} ,i n v e r t : f unc t i on ( x ) {

re turn [−x [ 0 ] ,−x [ 1 ] ] ;}

} , {wrap : f unc t i on ( x ) {

re turn x ? [ 1 , 1 ] : [ 0 , 1 ] ;} ,unwrap : f unc t i on ( x ) {

re turn x [ 0 ]==x [ 1 ] ;}

} ) ;

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce∗ / Cell

Особенностью работы reduce∗ является то, что он учитывает нетолько реактивность списка, но и реактивность переменных…

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce∗ / Cell

var l i s t = new L i s t ( ) ;var i t 1 = new Ce l l ( 0 ) ;var i t 2 = new Ce l l ( 1 ) ;var sum = l i s t . r educe ( 0 , f unc t i on ( a , b ) {

re turn a + b ;} ) ; // sum has 0var i t I d 1 = l i s t . add ( i t 1 ) ; // sum has 0var i t I d 2 = l i s t . add ( i t 2 ) ; // sum has 1i t 1 . s e t ( 5 ) ; // sum has 6i t 2 . s e t ( 2 ) ; // sum has 7i t 1 . unse t ( ) ; // sum is unsetl i s t . remove ( i t I d 1 ) ; // sum has 2

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce∗ / Cell / ignoreUnset

Видно, что если внутри реактивный списока есть пустаяпеременная, то результат агрегации будет пустым. Это

поведение можно изменить: достаточно в reduce передатьопцию ignoreUnset - все пустые элементы будут заменяться на

единичный элемент моноида или группы

var i t em 1 = new Ce l l ( ) ;var i t em 2 = new Ce l l ( 1 ) ;var l i s t = new L i s t ( [ i t em 1 , i t em 2 ] ) ;var sum = l i s t . r educe ( 0 , f unc t i on ( a , b ) {

re turn a + b ;} , {

i gno r eUnse t : t rue} ) ; // sum has 1i t em 1 . s e t ( 3 ) ; // sum has 4

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Спасибо

Wow! 65 слайдов, вы этовыдержали!