65
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Реактивные примитивы Warp9 @rystsov 2013

Warp9: reactive primitives

  • Upload
    rystsov

  • View
    16.559

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

@rystsov

2013

Page 2: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 3: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 4: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / ctor

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

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

var a = new Ce l l ( ) ;

Page 5: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / set

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

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

Page 6: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / ctor(…)

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

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

Page 7: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / unset

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

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

Page 8: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / unwrap

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

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

Page 9: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / unwrap(…)

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

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

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

Page 10: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 11: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / li

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

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

Page 12: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / li

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

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

Page 13: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / coalesce

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

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

умолчанию.

Page 14: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 15: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 16: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…)

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

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

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

Page 17: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 18: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when

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

равенство.

Page 19: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …)

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

Page 20: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 21: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 ) ;

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

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

Page 22: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 23: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / when(…, …, …)

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

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

Page 24: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 25: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 26: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 )} ) ;

Page 27: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind

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

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

Page 28: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind

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

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

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

Page 29: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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…

Page 30: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 ) ) ;

} ) ;}

Page 31: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 ( ) ;} ) ;

} ;

Page 32: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Cell / bind / binary

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

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

Page 33: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 34: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Монады

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

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

Page 35: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 36: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / ctor

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

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

Page 37: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / ctor(…)

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

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

Page 38: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / add

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

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

Page 39: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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"]

Page 40: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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"]

Page 41: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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" } ;} ) ;

Page 42: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 43: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 44: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / remove, removeWhich и forEach

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

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

Page 45: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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]

Page 46: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / liing

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

Page 47: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce

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

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

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

Page 48: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 получилась реактивной иизменяется при изменении списка.

Page 49: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / count

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

Page 50: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 51: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 ;}

} ) ;

Page 52: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 )

Page 53: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

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

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

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

Page 54: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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, и будет прав

Page 55: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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).Аналогично можно посчитать и кол-во элементов в списке

Page 56: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce∗

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

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

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

Page 57: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduceGroup

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

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

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

reduceGroup

Page 58: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduceMonoid

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

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

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

reduceGroup

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

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

Page 59: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce(&)

Хотя я вру

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

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

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

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

Page 60: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce / unwrap

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

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

Page 61: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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 ] ;}

} ) ;

Page 62: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

List / reduce∗ / Cell

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

Page 63: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 64: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

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

Page 65: Warp9: reactive primitives

..........

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

.....

.....

......

.....

......

.....

.....

.

Спасибо

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