Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 06.11.07 14:18
Оценка: 93 (8)
Предыстория и рассказ, откуда взялась задача Problem K — здесь: О дизайне и ФП
Решаем задачку Problem K — код выложу позже.

Итак, как же будет выглядеть дизайн решения задачи Problem K (http://thesz.livejournal.com/280784.html) в случае Эрланга. Для начала разберемся, какие возможности в плане декомпозиции нам дает Эрланг.

В Эрланге, собственно говоря, есть два основных инструмента для структурирования системы. Первое — модули, второе — процессы. Процесс с точки зрения дизайна является прямым аналогом ООП-шного _объекта_. Он полностью удовлетворяет букве и духу определения объекта Алана Кея. Он имеет состояние (на стеке в бесконечной рекурсии), имеет идентити (pid()), посылает и отправляет сообщения другим процессам, а также сам решает, как обработать полученное сообщение. Кроме того, если сравнить процессы Эрланга с объектами Smalltalk 72, мы получим _полнейшую_ идентичность. Системы не похожи — они просто одинаковы. В случае Эрланга мы, правда, имеем добавочные свойства в виде распределенности, изоляции, и отказоустойчивости. Но это в контексте нашей темы — неважно.

Итак, процессы в Эрланге могут применяться (и применяются) в тех же контекстах и с теми же целями, как объекты в ООП. Кроме того, процессы в Эрланге могут применяться и по другому — как эквивалент техники декомпозиции программы на ленивых списках (потоках) в чистых языках. Таким образом, процессы в Эрланге обеспечивают большую гибкость при декомпозиции задачи, и являются редкой техникой, сочетающей свойства как "потокового" dataflow дизайна, традиционого для "чистых" языков, так и ОО дизайна. В курсе SICP авторы отмечали, что задача "подружить" два этих "ортогональных" подхода — это сложная и актуальная теоретическая проблема. Так вот, процессы Эрланга (а точнее — actors model) — это ее решение, это более общий механизм.

Однако, мы не будем их применять при решении нашей задачи — неспортивно это, и ограничимся применением модулей. Потому, что мы хотим от нашего решения функциональной чистоты. В этом случае, оно будет идиот... (черт, какие умные слова ты употребляешь, thesz!) идиоматическим не только для Эрланга, как хотел thesz, но также для большинства функциональных языков.

Так вот, модуль — это очень простая штука. Это файл, единица раздельной компиляции. Он имеет имя. Он может экспортировать определенные в нем функции. Они вызываются вот так: module_name:function_name( arg1, ..., argN ). И все.

Таким образом, задача дизайна для нас сводится к очень простой штуке. Надо побить наше решение на модули, таким образом, чтобы связность между ними была настолько слаба, насколько это возможно. Да, важный момент — никакого "состояния", в отличии от классов, модули не инкапсулируют. Это просто некоторая именованная группировка функций.

Как мы можем уменьшить связность между модулями? Это, право же, просто. Воспользуемся концепцией "абстрактного типа данных". Суть состоит в том, что мы будем прятать структуры данных за функциональными интерфейсами. Ну-с, приступим-с. Начнем с наиболее простого и очевидного — бинарных операций. Их неплохо бы завернуть в отдельный модуль — если я сделаю это правильно, то я смогу попросить, скажем, potan-а, добавить новую операцию не заставляя его разбираться с моим дизайном, и он это сделает очень быстро, совершенно не забивая голову никаким дизайном. Собственно, это основное свойство хорошего дизайна. Не верите?

-module( operation ).
-export( [ tokens/0, create/1, execute/3 ] ).

%% tokens() -> [ char() ].
%% binary operators' token list...
tokens() -> "+-*/".

%% create( char() ) -> bin_op()
%% create binary operator...
create( $+ ) -> fun add/2;
create( $- ) -> fun sub/2;
create( $* ) -> fun mult/2;
create( $/ ) -> fun divd/2.

%% execute( bin_op(), term(), term() ) -> term() | { error, atom() }.
%% Execute binary operation...
execute( Op, A, B ) when is_integer( A ), is_integer( B ) -> Op( A, B );
execute( _, _, _ ) -> { error, '#evaluation' }.

%% Binary operations implementation... 
add( X, Y )  -> X + Y.
mult( X, Y ) -> X * Y.
sub( X, Y ) -> X - Y.

divd( _, 0 ) -> { error, '#division by zero' };
divd( X, Y ) -> X / Y.


Вот так. tokens возвращает список токенов для бинарных операций, create создает из токена операцию, execute ее выполняет. Здорово. Почему мы выбрали для имени модуля существутельное? Давайте посмотрим, как быдут выглядеть вызовы:

Op = operation:create( $+ ).
operation:execute( Op, 1, 2 ).

Просто, красиво, и понятно. И главное — когда мы будем пользоваться этим модулем, нас совершенно не будет волновать, как именно реализована операция. Она у нас АТД. Поэтому, когда мы будем в последствии добавлять операции над строками (часть условия задачи), мы ограничимся модификацией только этого простого модуля. Более того — мы эту модификацию можем кому-нибудь поручить, и он быстро с ней справится.

Теперь я опишу дизайн всего решения. Я его оцениваю примерно на 4-ку, то есть, в принципе — good enough.

sheet — это таблица, ее можно создавать, рассчитывать, заполнять клетками, и получать клетки.

Она использует cell — это клетка, которая умеет заполняться из текста и преобразовываться в текст, а также умеет рассчитывать свое значение, возможно, изменяя при этом sheet.

Последнее она делает при помощи expression, который умеет создаваться из текста, и вычислять свое значение, при этом, модифицируя sheet. expression вычисляет свои значения при помощи модуля

operation, который реализует вычисление операций.

Еще будет модуль main, который у нас будет "грязный", и он знает, как сохранять и читать таблицу работая со стандартным вводом/выводом. Он взаимодействует только с sheet.

-module( operation ).
-export( [ tokens/0, create/1, execute/3 ] ).
%% Бинарная операция. Можно ее создать, можно выполнить.

tokens() -> [ char() ].
%% binary operators' token list...

create( char() ) -> bin_op()
%% create binary operator...

execute( bin_op(), term(), term() ) -> term() | { error, atom() }.
%% Execute binary operation...

-module( expression ).
-export( [ create/1, evaluate/3 ] ).
%% Выражение. Можно его создать, можно вычислить. Вычисление выражения изменяет таблицу.
%% uses: operation, sheet, cell

create( string() ) -> expression().
%% create expression from string...
%% uses: operation
    
evaluate( Ref, Expression, Sheet ) -> { Value, UpdatedSheet }.
%% evaluate expression...
%% uses: operation, sheet, cell

-module( cell ).
-export( [    create/1, to_string/1, evaluate/2, %% внешний интерфейс 
             eval_and_get/2 ] ). %% Интерфейс для expression
%% Ячейка таблицы
%% uses: expression, sheet

create( string() ) -> cell()
%% create cell from text representation
%% uses: expression

to_string( value() ) -> string().
%% get text representation for the given cell value. Can't handle expressions.

evaluate( Ref, sheet() ) -> sheet().
%% Evaluate cell for the given reference.

eval_and_get( Ref, sheet() ) -> { Value, sheet() }.
%% Evaluate cell and get its value...
%% uses: sheet, expression

-module( sheet ). %% главный модуль - АТД для спредшыта. Его по-хорошему надо на два побить.
-export( [    new/0, set_cell/3, get_cell/2, %% Это интерфейс для нашего вычислительного движка.
        update_row/3, get_row/3, evaluate/1 ] ). %% Это внешний интерфейс для всего спредшыта

new() -> sheet()
%% construct new sheet...

set_cell( Ref, cell(), sheet() ) -> sheet().
%% Ref = { Row, Col }
%% Row = Col = integer()

get_cell( Ref, sheet() ) -> Value.

evaluate( sheet() ) -> sheet().
%% evaluate all expressions...

update_row( Row, [ string() ], sheet() ) -> sheet()
%% update row with a given list of cells

get_row( Row, Cols, sheet() ) -> [ string() ]


Итого, имеем следующие связи модулей (ниже посмотрите детали определения модулей, будет видно, почему так).

main -> sheet <-> cell <-> expression -> operation
          ^-------------------+


Потом нарисую красивую картинку.

Похоже на ОО декомпозицию, правда? Однако — что есть критерий разделения на модули? Ну не инкапсулированное состояние же, в конце концов, как в ООП, нет у нас никакого состояния, программа "чиста". Критерий — вот что. За границами модуля мы не обязаны знать ничего про его внутренние структуры данных. Вот что является критерием.
Re: Решение Problem K на Эрланг: дизайн
От: Трурль  
Дата: 07.11.07 10:56
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Надо побить наше решение на модули, таким образом, чтобы связность между ними была настолько слаба, насколько это возможно.


G>Итого, имеем следующие связи модулей (ниже посмотрите детали определения модулей, будет видно, почему так).


G>
G>main -> sheet <-> cell <-> expression -> operation
G>          ^-------------------+
G>


G>Может я чего-то не учел, но связи можно сделать и попроще.


main -> sheet -> cell -> expression -> operation
Re[2]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 12:20
Оценка:
Здравствуйте, Трурль, Вы писали:

G>>Может я чего-то не учел, но связи можно сделать и попроще.


Т>
Т>main -> sheet -> cell -> expression -> operation
Т>


Так не получится. Вся сложность в алгоритме expression. Он вычисляет клетки рекурсивно. Когда он встречает в аргументе reference, ему надо вычислить значение в клетке, а там также может быть expression. Отсюда протягивается обратная ссылка на cell. Придется мне привести код, чтобы стало понятно.

Кстати, приглашаю поиграть в "пятнашки". Помоги мне, попробуй улучшить дизайн. Принципы дизайна, которые надо сохранить — очень простые: если ты посмотришь внимательно на код, то ты обратишь внимание, что модули не знают про структуры данных друг друга — между ними ходят либо абстрактные, либо очень простые типы данных. Предлагается это свойство сохранить, и попытаться уменьшить связность.

-module( expression ).
-export( [ create/1, evaluate/3 ] ).

%% create( string() ) -> expression().
%% create expression from text...
create( Expr ) ->
    Pos = string:cspan( Expr, operation:tokens() ),
    case lists:split( Pos, Expr ) of
        { Val, [ Op | Rest ] } ->
            { create_arg( Val ), operation:create( Op ), create( Rest ) };
        { Val, [] } -> create_arg( Val )
    end.

%% create_arg( string() ) -> Ref | integer() | Error
%% Ref = { integer(), integer() }
%% Error = { error, atom() }
create_arg( [ Row, Col ] ) when Row >= $a, Row =< $z, Col >= $0, Col =< $9 ->
    { ref, { Row - $a + 1, Col - $0 } };
create_arg( Number ) -> cell:create_number( Number ).
    
%% evaluate( Ref, Expression, Sheet ) -> { Value, UpdatedSheet }.
%% evaluate expression...
evaluate( _, updating, Sheet ) -> { { error, '#cycle' }, Sheet };
evaluate( Ref, Expr, Sheet ) ->
    Sheet_1 = sheet:set_cell( Ref, updating, Sheet ),
    { Value, Sheet_2 } = evaluate( Expr, Sheet_1 ),
    { Value, sheet:set_cell( Ref, Value, Sheet_2 ) }.

%% evaluate( Expression, Sheet ) -> { Value, Sheet }
%% Evaluate expression. Forces evaluation of all referenced cells...
evaluate( { A, Op, B }, Sheet ) ->
    { ResA, Sheet_1 } = evaluate( A, Sheet ),
    { ResB, Sheet_2 } = evaluate( B, Sheet_1 ),
    { operation:execute( Op, ResA, ResB ), Sheet_2 };
evaluate( { ref, Ref }, Sheet ) -> cell:eval_and_get( Ref, Sheet );
evaluate( Val, Sheet ) -> { Val, Sheet }.


-module( cell ).
-export( [    create/1, create_number/1, to_string/1,
            evaluate/2, eval_and_get/2 ] ).

%% create( string() ) -> cell()
%% create cell from text representation
create( "" ) -> "";
create( [ $' | Text ] ) -> Text;
create( [ $= | Expr ] ) -> { expr, expression:create( Expr ) };
create( Number ) -> create_number( Number ).

%% create_number( string() ) -> integer() | { error, '#nan' }.
%% create number from string...
create_number( Number ) -> 
    case string:to_integer( Number ) of
        { Value, [] } -> Value;
        _ -> { error, '#nan' }
    end.

%% to_string( value() ) -> string().
%% get text representation for the given cell value
to_string( Int ) when is_integer( Int ) -> integer_to_list( Int );
to_string( { error, Msg } ) -> atom_to_list( Msg );
to_string( Text ) -> Text.

%% evaluate( Ref, sheet() ) -> sheet().
%% Evaluate cell value for the given reference
evaluate( Ref, Sheet ) -> 
    { _, Sheet_1 } = eval_and_get( Ref, Sheet ),
    Sheet_1.

%% eval_and_get( Ref, sheet() ) -> { Value, sheet() }.
%% Evaluate cell and get its value...
eval_and_get( Ref, Sheet ) ->
    case sheet:get_cell( Ref, Sheet ) of
        { expr, Expr } -> expression:evaluate( Ref, Expr, Sheet );
        Value -> { Value, Sheet }
    end.


-module( sheet ).
-export( [    new/0, set_cell/3, get_cell/2, 
            update_row/3, get_row/3, create_cell/2, cell_as_string/2, evaluate/1 ] ).

%% new() -> sheet()
%% construct new sheet...
new() -> gb_trees:empty().

%% set_cell( Ref, cell(), sheet() ) -> sheet().
%% Ref = { Row, Col }
%% Row = Col = integer()
set_cell( _, "", Sheet ) -> Sheet; %%% do NOT store empty values...
set_cell( Ref, Value, Sheet ) -> gb_trees:enter( Ref, Value, Sheet ).

%% get_cell( Ref, sheet() ) -> Value.
get_cell( Ref, Sheet ) -> 
    case gb_trees:lookup( Ref, Sheet ) of
        { value, Value } -> Value;
        _ -> ""
    end.

%% evaluate( sheet() ) -> sheet().
%% evaluate all expressions...
evaluate( Sheet ) -> lists:foldl( fun cell:evaluate/2, Sheet, gb_trees:keys( Sheet ) ).

%% update_row( Row, [ string() ], sheet() ) -> sheet()
%% update row 
update_row( Row, Cells, Sheet ) ->
    Refs = [ { Row, Col } || Col <- lists:seq( 1, lists:length( Cells ) ) ],
    lists:foldl( fun create_cell/2, Sheet, lists:zip( Refs, Cells ) ).

%% get_row( Row, Cols, sheet() ) -> [ string() ]
get_row( Row, Cols, Sheet ) ->
    [ cell_as_string( { Row, Col }, Sheet ) || Col <- lists:seq( 1, Cols ) ].

%% get_cell_text( Ref, sheet() ) -> string().
cell_as_string( Ref, Sheet ) -> cell:to_string( get_cell( Ref, Sheet ) ).

%% create_cell( { Ref, string() }, sheet() ) -> sheet().
create_cell( { Ref, Text }, Sheet ) -> set_cell( Ref, cell:create( Text ), Sheet ).
Re[2]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 12:24
Оценка:
Здравствуйте, Трурль, Вы писали:

Вообще — обрати внимание, насколько прост дизайн в sequentional erlang. Единственное, над чем надо думать — группировка функций по модулям. Это удивительно, настолько простого и эффективного подхода к дизайну я нигде еще не встречал. Крупно налажать в дизайне практически негде, рефакторинг элементарен.
Re[3]: Решение Problem K на Эрланг: дизайн
От: Курилка Россия http://kirya.narod.ru/
Дата: 07.11.07 12:32
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, Трурль, Вы писали:


G>Вообще — обрати внимание, насколько прост дизайн в sequentional erlang. Единственное, над чем надо думать — группировка функций по модулям. Это удивительно, настолько простого и эффективного подхода к дизайну я нигде еще не встречал. Крупно налажать в дизайне практически негде, рефакторинг элементарен.


А какие виды реально значимого рефакторинга для Эрланга можешь назвать?
Re[3]: дизайн-паттерны, Эрланг
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 12:37
Оценка: 8 (1)
А вот дизайн-паттерны, как это ни смешно, есть.

1) Абстрактный тип данных.
В модуле описывается функция конструктор абстрактного типа данных, а также функции-манипуляторы с ним. Пользователь модуля не знает внутреннее устройство типа данных. Пример — gb_trees из стандартной библиотеки. Собственно, у этого паттерна нулевой "синтаксический оверхэд", его можно наблюдать в моем коде.

2) "Динамический" абстрактный тип данных.
Если надо написать generic алгоритм, который оперирует на разных реализациях одного АТД, делается так. Состояние АТД — тупл, первый элемент которого — имя модуля, в котором определен АТД. Тогда:

type( ADT ) -> element( 0, ADT ).

dynamic_generic_function( ADT, Arg1, ..., ArgN ) -> type( ADT ):dynamically_dispatched_call( ADT, Arg1, ..., ArgN ).

3) behaviours. Примеры и документация есть в OTP.

Еще один способ динамической диспетчеризации более легковесный, он основан на использовании функций и приведен в моем модуле operation. На design pattern это, право слово, не тянет.
Re[4]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 12:46
Оценка: 16 (2)
Здравствуйте, Курилка, Вы писали:

К>Здравствуйте, Gaperton, Вы писали:


G>>Здравствуйте, Трурль, Вы писали:


G>>Вообще — обрати внимание, насколько прост дизайн в sequentional erlang. Единственное, над чем надо думать — группировка функций по модулям. Это удивительно, настолько простого и эффективного подхода к дизайну я нигде еще не встречал. Крупно налажать в дизайне практически негде, рефакторинг элементарен.


К>А какие виды реально значимого рефакторинга для Эрланга можешь назвать?


1) Передвинуть функцию из одного модуля в другой. Это тривиально, и может быть автоматизировано.
2) По другому переразбить кусочек кода на функции, с одновременным изменением местоположения функций по модулям. Собственно, я занимался этим в перерывах между работой несколько дней, выполнял глубокую перегруппировку раза 4. Это основное.
3) Накатить design pattern из моего соседнего поста на монолитный код. Одна проблема — паттерны элементарны, и все на самом деле сведется к пункту 2.

Это если говорить о sequentional erlang. А вот когда мы задействуем процессы, что в более крупной системе обязательно произойдет, вот тут-то самое интересное и начнется. Вот это будет уже больше похоже на геморройный дизайн, к которому мы привыкли. Но не попробуешь — не узнаешь.
Re[5]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 12:54
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, Курилка, Вы писали:


К>>Здравствуйте, Gaperton, Вы писали:


G>>>Здравствуйте, Трурль, Вы писали:


G>>>Вообще — обрати внимание, насколько прост дизайн в sequentional erlang. Единственное, над чем надо думать — группировка функций по модулям. Это удивительно, настолько простого и эффективного подхода к дизайну я нигде еще не встречал. Крупно налажать в дизайне практически негде, рефакторинг элементарен.


К>>А какие виды реально значимого рефакторинга для Эрланга можешь назвать?


G>1) Передвинуть функцию из одного модуля в другой. Это тривиально, и может быть автоматизировано.

G>2) По другому переразбить кусочек кода на функции, с одновременным изменением местоположения функций по модулям. Собственно, я занимался этим в перерывах между работой несколько дней, выполнял глубокую перегруппировку раза 4. Это основное.
G>3) Накатить design pattern из моего соседнего поста на монолитный код. Одна проблема — паттерны элементарны, и все на самом деле сведется к пункту 2.

G>Это если говорить о sequentional erlang. А вот когда мы задействуем процессы, что в более крупной системе обязательно произойдет, вот тут-то самое интересное и начнется. Вот это будет уже больше похоже на геморройный дизайн, к которому мы привыкли. Но не попробуешь — не узнаешь.


Вообще — общий вывод такой: функциональная чистота радикально упрощает дизайн — фактически, она устраняет его как геморройную и ответственную фазу, к которой мы привыкли в ОО. Конкретнее — спасает отсутствие указателей, референсов, и изменяемого состояния. Из-за этого рефакторинг обходится дешевле на порядок — его просто не замечаешь. И дизайн элементарен — это уже не настолько трудоемкая и ответственная фаза. Думать нужно только о связности и группировке функций. Халява полная.

А вот с процессами мы таки получим обратно изменяемое состояние и референсы. Тогда и начнется самое интересное.
Re[4]: дизайн-паттерны, Эрланг
От: Курилка Россия http://kirya.narod.ru/
Дата: 07.11.07 13:14
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>3) behaviours. Примеры и документация есть в OTP.


Единственное что не очень понравилось — композицию в смысле наследования (скажем банкомат, который ещё и сервер) из них делать не очень удобно, хотя это не сильно надо, не ООП ведь. Наверное лучше это делегированием решать
Re[5]: дизайн-паттерны, Эрланг
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 13:18
Оценка:
Здравствуйте, Курилка, Вы писали:

К>Здравствуйте, Gaperton, Вы писали:


G>>3) behaviours. Примеры и документация есть в OTP.


К>Единственное что не очень понравилось — композицию в смысле наследования (скажем банкомат, который ещё и сервер) из них делать не очень удобно, хотя это не сильно надо, не ООП ведь. Наверное лучше это делегированием решать


Ну, можно делать композицию и не в смысле наследования. Ты законченный пример приведи, в котором ты считаешь тебе наследование поможет. А мы посмотрим, как это сделать идио... матическим образом
Re[6]: дизайн-паттерны, Эрланг
От: Курилка Россия http://kirya.narod.ru/
Дата: 07.11.07 13:25
Оценка: 13 (1)
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, Курилка, Вы писали:


К>>Здравствуйте, Gaperton, Вы писали:


G>>>3) behaviours. Примеры и документация есть в OTP.


К>>Единственное что не очень понравилось — композицию в смысле наследования (скажем банкомат, который ещё и сервер) из них делать не очень удобно, хотя это не сильно надо, не ООП ведь. Наверное лучше это делегированием решать


G>Ну, можно делать композицию и не в смысле наследования. Ты законченный пример приведи, в котором ты считаешь тебе наследование поможет. А мы посмотрим, как это сделать идио... матическим образом


Да была задача аля http://www.trapexit.org/Cascading_Behaviours только у меня не fsm, а server был, и ничего, вполне себе работало.
Re[6]: Решение Problem K на Эрланг: дизайн
От: Cyberax Марс  
Дата: 07.11.07 13:34
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Вообще — общий вывод такой: функциональная чистота радикально упрощает дизайн — фактически, она устраняет его как геморройную и ответственную фазу, к которой мы привыкли в ОО. Конкретнее — спасает отсутствие указателей, референсов, и изменяемого состояния. Из-за этого рефакторинг обходится дешевле на порядок — его просто не замечаешь. И дизайн элементарен — это уже не настолько трудоемкая и ответственная фаза. Думать нужно только о связности и группировке функций. Халява полная.

Иногда, тем не менее, оно сильно мешает. Сейчас пишу на Эрланге модуль к ejabberd — в паре мест существенно помогла бы возможность иметь нормальные мутабельные переменные. Определенные типы рефакторингов (например, изменение форматов туплов) тоже не очень приятно делаются.

Так что не все так замечательно — очень хочется иметь статический язык, где была бы возможность использовать совместно мутабельные и иммутабельные типы.

G>А вот с процессами мы таки получим обратно изменяемое состояние и референсы. Тогда и начнется самое интересное.

Процесс вместо объекта в каждом случае, ИМХО, слишком — даже для Эрланга. Объекты в ОО — это самодостаточные легковесные сущности, а процессы в Эрланге все же находятся под контролем планировщика и требуют глобальной регистрации.
Sapienti sat!
Re[7]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 13:53
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Здравствуйте, Gaperton, Вы писали:


G>>Вообще — общий вывод такой: функциональная чистота радикально упрощает дизайн — фактически, она устраняет его как геморройную и ответственную фазу, к которой мы привыкли в ОО. Конкретнее — спасает отсутствие указателей, референсов, и изменяемого состояния. Из-за этого рефакторинг обходится дешевле на порядок — его просто не замечаешь. И дизайн элементарен — это уже не настолько трудоемкая и ответственная фаза. Думать нужно только о связности и группировке функций. Халява полная.

C>Иногда, тем не менее, оно сильно мешает. Сейчас пишу на Эрланге модуль к ejabberd — в паре мест существенно помогла бы возможность иметь нормальные мутабельные переменные.

В чем проблема? Используй словарь процесса. Если, конечно, ты не боишься .

C> Определенные типы рефакторингов (например, изменение форматов туплов) тоже не очень приятно делаются.

1) Делается это просто — достаточно найти все вхождения функции, возвращающей тупл, и сделать замену. Ничего особо неприятного — не сложнее, чем поменять местами параметры функции.
2) У тебя туплы сложного формата между модулями ходят? Это нехорошо, батенька. Не надо так делать — ошибка дизайна. А если без этого никак — в таких случаях erlang style guide рекомендует применять record-ы и делать декларации в отдельных модулях. А в пределах одного модуля изменить туплы — это не рефакторинг. Это фигня.

C> Так что не все так замечательно — очень хочется иметь статический язык, где была бы возможность использовать совместно мутабельные и иммутабельные типы.

Это языки группы ML. F#, OCaml, и прочее.

G>>А вот с процессами мы таки получим обратно изменяемое состояние и референсы. Тогда и начнется самое интересное.

C>Процесс вместо объекта в каждом случае, ИМХО, слишком — даже для Эрланга. Объекты в ОО — это самодостаточные легковесные сущности, а процессы в Эрланге все же находятся под контролем планировщика и требуют глобальной регистрации.

Его никто и не применяет в каждом случае. Общая философия такая — процессы применяются на для декомпозиции на макроуровне, чистый ФП стиль — на микроуровне.
Чтобы понять, в каких пределах можно применять процесс как объект — один процесс занимает порядка килобайта — их запросто можно сотню тысяч закрутить без деградации производительности. Короче, эрланговские процессы не треды, их можно не считать и не трястись над ними.
Re[7]: дизайн-паттерны, Эрланг
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 13:56
Оценка:
Здравствуйте, Курилка, Вы писали:

К>Здравствуйте, Gaperton, Вы писали:


G>>Здравствуйте, Курилка, Вы писали:


К>>>Здравствуйте, Gaperton, Вы писали:


G>>>>3) behaviours. Примеры и документация есть в OTP.


К>>>Единственное что не очень понравилось — композицию в смысле наследования (скажем банкомат, который ещё и сервер) из них делать не очень удобно, хотя это не сильно надо, не ООП ведь. Наверное лучше это делегированием решать


G>>Ну, можно делать композицию и не в смысле наследования. Ты законченный пример приведи, в котором ты считаешь тебе наследование поможет. А мы посмотрим, как это сделать идио... матическим образом


К>Да была задача аля http://www.trapexit.org/Cascading_Behaviours только у меня не fsm, а server был, и ничего, вполне себе работало.


Ну вот, тут уже все написано.

Result = Mod:handle_event(Event, ExtStateName, ExtStateData),

И все дела.
Re[3]: Код expression, sheet, cell: задачка
От: red75  
Дата: 07.11.07 14:05
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Кстати, приглашаю поиграть в "пятнашки". Помоги мне, попробуй улучшить дизайн. Принципы дизайна, которые надо сохранить — очень простые: если ты посмотришь внимательно на код, то ты обратишь внимание, что модули не знают про структуры данных друг друга — между ними ходят либо абстрактные, либо очень простые типы данных. Предлагается это свойство сохранить, и попытаться уменьшить связность.


Хм. А ведь Cell — это, в общем-то, простое хранилище, однозначно определяемое по {Ref,Table}. Почему-бы не вынести cell:evaluate, cell:eval_and_get в Table?
Re[8]: Решение Problem K на Эрланг: дизайн
От: Cyberax Марс  
Дата: 07.11.07 14:38
Оценка:
Здравствуйте, Gaperton, Вы писали:

C>>Иногда, тем не менее, оно сильно мешает. Сейчас пишу на Эрланге модуль к ejabberd — в паре мест существенно помогла бы возможность иметь нормальные мутабельные переменные.

G>В чем проблема? Используй словарь процесса. Если, конечно, ты не боишься .
Так приходится — некрасиво получается. Не по-Эрланговски, что ли.

C>> Определенные типы рефакторингов (например, изменение форматов туплов) тоже не очень приятно делаются.

G>1) Делается это просто — достаточно найти все вхождения функции, возвращающей тупл, и сделать замену. Ничего особо неприятного — не сложнее, чем поменять местами параметры функции.
У меня тупл передавался через несколько функций — поэтому одно использование я случайно пропустил. В результате, меня среди ночи разбудили звонками клиенты — у них все не работало после перехода на зимнее время

G>2) У тебя туплы сложного формата между модулями ходят? Это нехорошо, батенька. Не надо так делать — ошибка дизайна. А если без этого никак — в таких случаях erlang style guide рекомендует применять record-ы и делать декларации в отдельных модулях. А в пределах одного модуля изменить туплы — это не рефакторинг. Это фигня.

Не сложные, но случайное добавление/удаление поля из тупла в три элемента — может многое сломать.

C>> Так что не все так замечательно — очень хочется иметь статический язык, где была бы возможность использовать совместно мутабельные и иммутабельные типы.

G>Это языки группы ML. F#, OCaml, и прочее.
Я знаю. Мне реально подходит только OCaml, но я сколько ни пробовал его — все никак он у меня не приживается. Марсианский он какой-то.

У меня все есть мысль сделать микро-CLI на LLVM и портировать туда Nemerle. Но пока жду, что кто-нибудь наконец-то доведет поддержку GC в LLVM до вменяемого уровня

C>>Процесс вместо объекта в каждом случае, ИМХО, слишком — даже для Эрланга. Объекты в ОО — это самодостаточные легковесные сущности, а процессы в Эрланге все же находятся под контролем планировщика и требуют глобальной регистрации.

G>Его никто и не применяет в каждом случае. Общая философия такая — процессы применяются на для декомпозиции на макроуровне, чистый ФП стиль — на микроуровне.
Тут получается интересный вопрос — а где деление проводить? Это не всегда так четко понятно — у меня лично в первое время на Эрланге с этим проблемы были. Потом привык и стал писать более-менее нормально.

G>Чтобы понять, в каких пределах можно применять процесс как объект — один процесс занимает порядка килобайта — их запросто можно сотню тысяч закрутить без деградации производительности. Короче, эрланговские процессы не треды, их можно не считать и не трястись над ними.

Я это знаю. Но с другой стороны, эрланговские процессы — и не объекты. Интересно было бы еще подумать о системе, где процессы вели бы себя полностью как объекты — т.е. не имели глобальной регистрации. Хотя это Smalltalk опять получится.
Sapienti sat!
Re[8]: дизайн-паттерны, Эрланг
От: Курилка Россия http://kirya.narod.ru/
Дата: 07.11.07 14:48
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, Курилка, Вы писали:


К>>Да была задача аля http://www.trapexit.org/Cascading_Behaviours только у меня не fsm, а server был, и ничего, вполне себе работало.


G>Ну вот, тут уже все написано.


G> Result = Mod:handle_event(Event, ExtStateName, ExtStateData),


G>И все дела.


Дак яж не спорю, что не написано, просто для меня это далеко не тривиально было когда я задумался о такой задаче. Тут тебе надо Состояние в Состояние заворачивать по сути, ну и протаскивать, что на мой взгляд для понимания будет посложней чем class A extends B
Re[4]: Объяснение устройства cell
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 15:00
Оценка: :)
Здравствуйте, red75, Вы писали:

R>Здравствуйте, Gaperton, Вы писали:


G>>Кстати, приглашаю поиграть в "пятнашки". Помоги мне, попробуй улучшить дизайн. Принципы дизайна, которые надо сохранить — очень простые: если ты посмотришь внимательно на код, то ты обратишь внимание, что модули не знают про структуры данных друг друга — между ними ходят либо абстрактные, либо очень простые типы данных. Предлагается это свойство сохранить, и попытаться уменьшить связность.


R>Хм. А ведь Cell — это, в общем-то, простое хранилище, однозначно определяемое по {Ref,Table}. Почему-бы не вынести cell:evaluate, cell:eval_and_get в Table?


Склеить их в принципе можно. Однако, стоит объяснить, почему они разделены.

Cell — не хранилище. Этот модуль на самом деле скрывает тип данных "содержимое ячейки".

Вот этот тип:
cell() = Value | { expression, expression() }
Value = integer() | string() | { error, atom() }


За пределами модуля этот тип не видно — видно только его конструкторы. Таким образом, sheet никак не завязан на формат данных cell. Вот внешний интерфейс cell — все, что о нем знает sheet, что его можно создать из текста:

create( string() ) -> cell().

А также, преобразовать обратно в текст.

to_string( cell() ) -> string().

Почему внутри кроме прочего находится еще и eval? Потому, что он также завязан на внутреннюю структуру типа cell().

%% eval_and_get( Ref, sheet() ) -> { Value, sheet() }.
%% Evaluate cell and get its value...
eval_and_get( Ref, Sheet ) ->
    case sheet:get_cell( Ref, Sheet ) of
        { expr, Expr } -> expression:evaluate( Ref, Expr, Sheet );
        Value -> { Value, Sheet }
    end.

Только зная внутреннюю структуру типа cell, мы сможем понять, что внутри лежит формула. Перенеся этот код в модуль sheet, мы получим более сильную связность - потому что человек, разбирающийся в устройстве sheet, не сможет понять его, не разобравшись в деталях cell. А это именно то, чего мы хотим избежать.

Почему при этом вы отдельный модуль вынесен expression? Потому, что вычисление выражений - это отдельная песня, это можно делать очень по разному. И мы хотим иметь возможность изменять способ вычисления выражений, не затрагивая остальной код. expression - скрывает внутри себя другой тип:

[code]
expression() =  BinaryOp | Ref | integer()
BinaryOp = { expression(), operation(), expression() }
Ref = { integer(), integer() }


Однако, cell этого не знает, плевать cell-y, как устроен expression. Он знает, что expression можно создать
create( string() ) -> expression()
и вычислить
evaluate( Ref, expression(), sheet() ) -> { Value, sheet() }.

Вот, кстати, тут виден реальный прощот. Value и Ref проходят явно в межмодульном интерфейсе. Вот это — ай-ай-ай. cell и expression слишком сильно связаны. Буду думать, как править.
Re[9]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 15:45
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>>> Определенные типы рефакторингов (например, изменение форматов туплов) тоже не очень приятно делаются.

G>>1) Делается это просто — достаточно найти все вхождения функции, возвращающей тупл, и сделать замену. Ничего особо неприятного — не сложнее, чем поменять местами параметры функции.
C>У меня тупл передавался через несколько функций — поэтому одно использование я случайно пропустил. В результате, меня среди ночи разбудили звонками клиенты — у них все не работало после перехода на зимнее время

Бывает. Я тоже накушался. Тестить надо, это же динамика. Хотя хиндли-милнер определенно гарантирует от таких ошибок.

Диалайзер кстати свежий, с аннотациями типов, не пробовал? Есть мнение, он должен радикально помочь.

G>>2) У тебя туплы сложного формата между модулями ходят? Это нехорошо, батенька. Не надо так делать — ошибка дизайна. А если без этого никак — в таких случаях erlang style guide рекомендует применять record-ы и делать декларации в отдельных модулях. А в пределах одного модуля изменить туплы — это не рефакторинг. Это фигня.

C>Не сложные, но случайное добавление/удаление поля из тупла в три элемента — может многое сломать.

Когда это происходит в пределах модуля — не страшно, это очень легко проверить. Ну, и без автоматических тестов, разумеется, никуда.

C>>> Так что не все так замечательно — очень хочется иметь статический язык, где была бы возможность использовать совместно мутабельные и иммутабельные типы.

G>>Это языки группы ML. F#, OCaml, и прочее.
C>Я знаю. Мне реально подходит только OCaml, но я сколько ни пробовал его — все никак он у меня не приживается. Марсианский он какой-то.
Ага, меня он тоже бесит.

C>У меня все есть мысль сделать микро-CLI на LLVM и портировать туда Nemerle. Но пока жду, что кто-нибудь наконец-то доведет поддержку GC в LLVM до вменяемого уровня


Не знаю, не знаю . Я бы так не делал ни в коем случае.

C>>>Процесс вместо объекта в каждом случае, ИМХО, слишком — даже для Эрланга. Объекты в ОО — это самодостаточные легковесные сущности, а процессы в Эрланге все же находятся под контролем планировщика и требуют глобальной регистрации.

G>>Его никто и не применяет в каждом случае. Общая философия такая — процессы применяются на для декомпозиции на макроуровне, чистый ФП стиль — на микроуровне.
C>Тут получается интересный вопрос — а где деление проводить? Это не всегда так четко понятно — у меня лично в первое время на Эрланге с этим проблемы были. Потом привык и стал писать более-менее нормально.

Пока не знаю. Интуитивно как-то получается. Та же проблема будет у тебя и в Nemerle, кстати.

G>>Чтобы понять, в каких пределах можно применять процесс как объект — один процесс занимает порядка килобайта — их запросто можно сотню тысяч закрутить без деградации производительности. Короче, эрланговские процессы не треды, их можно не считать и не трястись над ними.

C>Я это знаю. Но с другой стороны, эрланговские процессы — и не объекты. Интересно было бы еще подумать о системе, где процессы вели бы себя полностью как объекты — т.е. не имели глобальной регистрации. Хотя это Smalltalk опять получится.

В каком смысле регистрация глобальная и почему это тебя волнует?
Re[5]: Объяснение устройства cell
От: red75  
Дата: 07.11.07 16:18
Оценка: 26 (1)
Здравствуйте, Gaperton, Вы писали:

R>>Хм. А ведь Cell — это, в общем-то, простое хранилище, однозначно определяемое по {Ref,Table}. Почему-бы не вынести cell:evaluate, cell:eval_and_get в Table?


G>Склеить их в принципе можно. Однако, стоит объяснить, почему они разделены.


Нет, я не говорю о склеивании. Связь Cell->Table осуществляется только через table:get_cell, но зачем Cell'у знать как получать значения из таблицы? Это нужно только для expression.

G>Cell — не хранилище. Этот модуль на самом деле скрывает тип данных "содержимое ячейки".


Да, я это и имел в виду. АТД абстрагирующий хранение содержимого ячейки.

G>Почему внутри кроме прочего находится еще и eval? Потому, что он также завязан на внутреннюю структуру типа cell().


G>
G>%% eval_and_get( Ref, sheet() ) -> { Value, sheet() }.
G>%% Evaluate cell and get its value...
G>eval_and_get( Ref, Sheet ) ->
G>    case sheet:get_cell( Ref, Sheet ) of
G>        { expr, Expr } -> expression:evaluate( Ref, Expr, Sheet );
G>        Value -> { Value, Sheet }
G>    end.
G>


G>Только зная внутреннюю структуру типа cell, мы сможем понять, что внутри лежит формула. Перенеся этот код в модуль sheet, мы получим более сильную связность — потому что человек, разбирающийся в устройстве sheet, не сможет понять его, не разобравшись в деталях cell. А это именно то, чего мы хотим избежать.


Я имел в виду:
-module(expression).
evaluate( { ref, Ref }, Sheet ) -> table:evaluate( Ref, Sheet );
-module(table).
evaluate(Ref,Sheet)->cell:evaluate(Ref,get_cell(Ref,Sheet)).
-module(cell).
evaluate(Ref,Cell)->и т.д.


G>Однако, cell этого не знает, плевать cell-y, как устроен expression. Он знает, что expression можно создать

G>create( string() ) -> expression()
G>и вычислить
G>evaluate( Ref, expression(), sheet() ) -> { Value, sheet() }.

G>Вот, кстати, тут виден реальный прощот. Value и Ref проходят явно в межмодульном интерфейсе. Вот это — ай-ай-ай. cell и expression слишком сильно связаны. Буду думать, как править.


Избавиться от передачи Ref'ов по-моему нельзя. Может запромотить его в АТД? Что-то вроде:
-module(table).
%% make_ref::(integer(),integer())->ref()
make_ref(X,Y)->{X,Y}.
-module(expression).
create_arg( [ Row, Col ] ) when Row >= $a, Row =< $z, Col >= $0, Col =< $9 ->
    { ref, table:make_ref(Row - $a + 1, Col - $0) };


А на счёт Value бродят смутные мысли о разделении значения ячейки (формула/литерал/пусто) и вычисленного значения ячейки (число/строка/ошибка), но пока не оформились.
Re[6]: Решение Problem K на Эрланг: дизайн
От: BulatZiganshin  
Дата: 07.11.07 16:41
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Вообще — общий вывод такой: функциональная чистота радикально упрощает дизайн — фактически, она устраняет его как геморройную и ответственную фазу, к которой мы привыкли в ОО. Конкретнее — спасает отсутствие указателей, референсов, и изменяемого состояния. Из-за этого рефакторинг обходится дешевле на порядок — его просто не замечаешь. И дизайн элементарен — это уже не настолько трудоемкая и ответственная фаза. Думать нужно только о связности и группировке функций. Халява полная.


G>А вот с процессами мы таки получим обратно изменяемое состояние и референсы. Тогда и начнется самое интересное.


а хвостовая рекурсия? получил сигнал, пересчитал состояние и всё:

-- ход противника
chess state = do move <- getChannel c
                 chess2 (applyMove state move)

-- наш ход
chess2 state = do let move = calcOurMove state
                  putChannel c move
                  chess (applyMove state move)
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 16:49
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>Здравствуйте, Gaperton, Вы писали:


G>>Вообще — общий вывод такой: функциональная чистота радикально упрощает дизайн — фактически, она устраняет его как геморройную и ответственную фазу, к которой мы привыкли в ОО. Конкретнее — спасает отсутствие указателей, референсов, и изменяемого состояния. Из-за этого рефакторинг обходится дешевле на порядок — его просто не замечаешь. И дизайн элементарен — это уже не настолько трудоемкая и ответственная фаза. Думать нужно только о связности и группировке функций. Халява полная.


G>>А вот с процессами мы таки получим обратно изменяемое состояние и референсы. Тогда и начнется самое интересное.


BZ>а хвостовая рекурсия? получил сигнал, пересчитал состояние и всё:


BZ>
BZ>-- ход противника
BZ>chess state = do move <- getChannel c
BZ>                 chess2 (applyMove state move)

BZ>-- наш ход
BZ>chess2 state = do let move = calcOurMove state
BZ>                  putChannel c move
BZ>                  chess (applyMove state move)
BZ>


Это чистый код. Тут ссылок нет. В Эрланге у процесса есть identity — pid, зная который, ты можешь послать ему сообщение. Этот pid можно передавать другим процессам. А у тебя здесь identity нет. Нет identity — нет агрегирования, владения, связей, и прочих проблем.
Re[6]: Объяснение устройства cell
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 17:08
Оценка:
Здравствуйте, red75, Вы писали:

R>Здравствуйте, Gaperton, Вы писали:


R>>>Хм. А ведь Cell — это, в общем-то, простое хранилище, однозначно определяемое по {Ref,Table}. Почему-бы не вынести cell:evaluate, cell:eval_and_get в Table?


G>>Склеить их в принципе можно. Однако, стоит объяснить, почему они разделены.


R>Нет, я не говорю о склеивании. Связь Cell->Table осуществляется только через table:get_cell, но зачем Cell'у знать как получать значения из таблицы? Это нужно только для expression.


Интересно. А ведь и в самом деле.

G>>Только зная внутреннюю структуру типа cell, мы сможем понять, что внутри лежит формула. Перенеся этот код в модуль sheet, мы получим более сильную связность — потому что человек, разбирающийся в устройстве sheet, не сможет понять его, не разобравшись в деталях cell. А это именно то, чего мы хотим избежать.


R>Я имел в виду:

R>
R>-module(expression).
R>evaluate( { ref, Ref }, Sheet ) -> table:evaluate( Ref, Sheet );
R>-module(table).
R>evaluate(Ref,Sheet)->cell:evaluate(Ref,get_cell(Ref,Sheet)).
R>-module(cell).
R>evaluate(Ref,Cell)->и т.д.
R>


Хм-м-м... В результате одним махом убираем связь от cell к sheet, и от expression к cell. Гениально. Отлично, спасибо!

G>>Однако, cell этого не знает, плевать cell-y, как устроен expression. Он знает, что expression можно создать

G>>create( string() ) -> expression()
G>>и вычислить
G>>evaluate( Ref, expression(), sheet() ) -> { Value, sheet() }.

G>>Вот, кстати, тут виден реальный прощот. Value и Ref проходят явно в межмодульном интерфейсе. Вот это — ай-ай-ай. cell и expression слишком сильно связаны. Буду думать, как править.


R>Избавиться от передачи Ref'ов по-моему нельзя. Может запромотить его в АТД? Что-то вроде:

R>
R>-module(table).
R>%% make_ref::(integer(),integer())->ref()
R>make_ref(X,Y)->{X,Y}.
R>-module(expression).
R>create_arg( [ Row, Col ] ) when Row >= $a, Row =< $z, Col >= $0, Col =< $9 ->
R>    { ref, table:make_ref(Row - $a + 1, Col - $0) };
R>


Да, именно так я и собрался сделать. Т.е. в точности так. Вплоть до имен функций и модулей .

R>А на счёт Value бродят смутные мысли о разделении значения ячейки (формула/литерал/пусто) и вычисленного значения ячейки (число/строка/ошибка), но пока не оформились.


Именно такие мысли ходили и у меня. В первых внутримодульных тайпдефах у меня это так и разделялось — UnevaluatedCell и Cell. Однако красиво завернуть это в АДТ — я прощелкал. Просто пропустил. Заметил только сейчас, когда начал объяснять.

Вот, с твоей правкой по поводу evaluate + с решенными проблемами Value и Ref, можно будет поднять оценку дизайна с 4 (good enough) до 5 (perfect).

Прикольно дизайнить для Эрланга, правда? Ничего нет кроме модулей и функций, а насколько дофига оказывается можно сделать! Даже налажать можно .
Re[8]: Решение Problem K на Эрланг: дизайн
От: BulatZiganshin  
Дата: 07.11.07 17:37
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Это чистый код. Тут ссылок нет.


именно об этом я и говорю

>В Эрланге у процесса есть identity — pid, зная который, ты можешь послать ему сообщение. Этот pid можно передавать другим процессам. А у тебя здесь identity нет. Нет identity — нет агрегирования, владения, связей, и прочих проблем.


а на пальцах показать? с чистыми данными такая проблема — это и есть данные. хочешь — шахматную доску передавай, хочешь — состояние ядерного реактора. "не будет ни радио, ни газет, ни кино — одно сплощное телевидение"
Люди, я люблю вас! Будьте бдительны!!!
Re[9]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 18:34
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>Здравствуйте, Gaperton, Вы писали:


G>>Это чистый код. Тут ссылок нет.


BZ>именно об этом я и говорю


>>В Эрланге у процесса есть identity — pid, зная который, ты можешь послать ему сообщение. Этот pid можно передавать другим процессам. А у тебя здесь identity нет. Нет identity — нет агрегирования, владения, связей, и прочих проблем.


BZ>а на пальцах показать? с чистыми данными такая проблема — это и есть данные. хочешь — шахматную доску передавай, хочешь — состояние ядерного реактора. "не будет ни радио, ни газет, ни кино — одно сплощное телевидение"



-module( my_object ).

-record( this, { member1 = "a", member2 = 1, ... } ).

new() ->spawn( fun dispatch/1, #members{} ).

set( This, Attribute, Value ) -> This ! { set, Attribute, Value }.
get( This, Attribute, Value ) ->
   This ! { get, Attribute, self() },
   receive { return, This, Value } -> Value end.

dispatch( Members ) ->
   NewState = receive
      { set, member1, Value } -> Members#this{ member1 = Value };
      { get, member1, Caller } -> Caller ! { return, self(), Members#this.member1 }, Members;
   ...
   end,
   dispatch( NewState ).

-module( caller ).
...
   Obj = my_object:new(),
   "a" = my_object:get( Obj, member1 )
   my_object:set( Obj, member1, "b" ),
...
   "a" = my_object:get( Obj, member1 ), %% Ой! Эксцепшн bad_match! Значение то у нас вернулось - "b"!
Re[3]: Код expression, sheet, cell: задачка
От: Трурль  
Дата: 07.11.07 19:10
Оценка: 39 (1)
Здравствуйте, Gaperton, Вы писали:

G>Так не получится. Вся сложность в алгоритме expression. Он вычисляет клетки рекурсивно. Когда он встречает в аргументе reference, ему надо вычислить значение в клетке, а там также может быть expression. Отсюда протягивается обратная ссылка на cell.


Каюсь, первая мысль была передавать в expression:evaluate функцию-разыменователь
evaluate(expression(), Ref->Value ) -> Value.

Это, конечно, плохо – нельзя исключить перевычислений и к тому же ни разу не идиоматически.

Но что если дать возможность узнать, на что ссылается выражение и передавать контекст для его вычисления?
module( expression ).
-export( [ create/1, evaluate/2, refs/1 ] ).

create( string() ) -> expression().

refs(expression()) -> [ Ref ].
evaluate(expression(), dictionary() ) -> Val.

А обновлениями ячеек и порядком вычислений пусть рулит sheet.
Re[4]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 19:43
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Но что если дать возможность узнать, на что ссылается выражение и передавать контекст для его вычисления?

Т>
Т>module( expression ).
Т>-export( [ create/1, evaluate/2, refs/1 ] ).

Т>create( string() ) -> expression().

Т>refs(expression()) -> [ Ref ].
Т>evaluate(expression(), dictionary() ) -> Val.
Т>

Т>А обновлениями ячеек и порядком вычислений пусть рулит sheet.

Неплохо. Я с самого начала мельком подумал о таком подходе, но почему-то сразу его отмел и решил делать тупо — тот же алгоритм, как в плюсах. Однако, получается интересно.

Можно обойтись и простым списком значений — без dictionary. Тогда реально все вытягивается в трубу. Ты имеешь в виду вот это?

-module( sheet ).

evaluate( Ref, Sheet ) ->
   Value = get_cell( Ref, Sheet ), % цыклы не ловим, ломает. Понятно, как, но лень
   { Context, NewSheet }= mapfoldl( fun evaluate/2, Sheet, cell:dependency_list( Value ) ),
   NewValue = cell:evaluate( Value, Context ),
   { NewValue, set_cell( Ref, NewValue ) }.
...

-module( cell ). %% как-то больно тупо стал выглядеть этот модуль. :)
depencency_list( { expression, Expr } ) -> expression:depencency_list( Expr );
depencency_list( _ ) -> [].

evaluate( { expression, Expr }, Context ) -> expression:evaluate( Expr, Context );
evaluate( Value, [] ) -> Value.

...

-module( expression ).

depencency_list( { _Expr, Refs } ) -> Refs.
evaluate( { ref, _ }, [ Value | Rest ] ) -> { Value, Context }
...
evaluate( Num, Context ) -> { Num, Context }
...


Это решение мне нравится существенно больше лобовой реализации типического плюсового алгоритма, как у меня. Попробую реализовать и этот вариант, как будет время.

Декоративное программирование рулит!
Re[10]: Решение Problem K на Эрланг: дизайн
От: BulatZiganshin  
Дата: 07.11.07 19:53
Оценка:
Здравствуйте, Gaperton, Вы писали:

BZ>>а на пальцах показать? с чистыми данными такая проблема — это и есть данные. хочешь — шахматную доску передавай, хочешь — состояние ядерного реактора. "не будет ни радио, ни газет, ни кино — одно сплощное телевидение"


для меня этот некомментированный код — не на пальцах
Люди, я люблю вас! Будьте бдительны!!!
Re[11]: Решение Problem K на Эрланг: дизайн
От: Gaperton http://gaperton.livejournal.com
Дата: 07.11.07 20:46
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

BZ>Здравствуйте, Gaperton, Вы писали:


BZ>>>а на пальцах показать? с чистыми данными такая проблема — это и есть данные. хочешь — шахматную доску передавай, хочешь — состояние ядерного реактора. "не будет ни радио, ни газет, ни кино — одно сплощное телевидение"


BZ>для меня этот некомментированный код — не на пальцах


spawn — создает процесс, берет параметром функцию, которая будет вызвана этим процессом, возвращает PID.

PID ! Message — отправляет процессу PID сообщение Message

receive — оператор блокирующего ожидания сообщения, соответствующего заданному паттерну.

Подробности в Эрланг референс мануал.
http://erlang.org/doc/reference_manual/part_frame.html
Re[7]: Объяснение устройства cell
От: red75  
Дата: 08.11.07 05:14
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Да, именно так я и собрался сделать. Т.е. в точности так. Вплоть до имен функций и модулей .




R>>А на счёт Value бродят смутные мысли о разделении значения ячейки (формула/литерал/пусто) и вычисленного значения ячейки (число/строка/ошибка), но пока не оформились.


G>Именно такие мысли ходили и у меня. В первых внутримодульных тайпдефах у меня это так и разделялось — UnevaluatedCell и Cell. Однако красиво завернуть это в АДТ — я прощелкал. Просто пропустил. Заметил только сейчас, когда начал объяснять.


G>Вот, с твоей правкой по поводу evaluate + с решенными проблемами Value и Ref, можно будет поднять оценку дизайна с 4 (good enough) до 5 (perfect).


Насчёт Value: ими ведь занимается только Expression? Добавить туда (в expression) конструкторы Value и преобразование Value->string, ну а Cell будет хранить Value|expression().

G>Прикольно дизайнить для Эрланга, правда? Ничего нет кроме модулей и функций, а насколько дофига оказывается можно сделать! Даже налажать можно .


Эт точно. Впрочем минимизация связности — NP-задача. ХЗ как мы её вообще решаем .
Re[8]: Объяснение устройства cell
От: Курилка Россия http://kirya.narod.ru/
Дата: 08.11.07 06:25
Оценка:
Здравствуйте, red75, Вы писали:

R>Здравствуйте, Gaperton, Вы писали:


G>>Прикольно дизайнить для Эрланга, правда? Ничего нет кроме модулей и функций, а насколько дофига оказывается можно сделать! Даже налажать можно .


R>Эт точно. Впрочем минимизация связности — NP-задача. ХЗ как мы её вообще решаем .


А вообще, интересно есть какие-либо исследования/книги посвящённые этой теме? Что-нибудь с обобщённой точки зрения на вопрос, а не конретика по языкам и т.п.?
Или в основном это на базе интуиции? Но даже если так, то должны же быть какие-нибудь эвристики или типа того...
Re[3]: Код expression, sheet, cell: задачка
От: sergesokolov Россия http://www.ideashag.spb.ru
Дата: 08.11.07 07:22
Оценка: -1
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, Трурль, Вы писали:


G>>>Может я чего-то не учел, но связи можно сделать и попроще.


Т>>
Т>>main -> sheet -> cell -> expression -> operation
Т>>


G>Так не получится. Вся сложность в алгоритме expression. Он вычисляет клетки рекурсивно. Когда он встречает в аргументе reference, ему надо вычислить значение в клетке, а там также может быть expression. Отсюда протягивается обратная ссылка на cell. Придется мне привести код, чтобы стало понятно.


G>Кстати, приглашаю поиграть в "пятнашки". Помоги мне, попробуй улучшить дизайн. Принципы дизайна, которые надо сохранить — очень простые: если ты посмотришь внимательно на код, то ты обратишь внимание, что модули не знают про структуры данных друг друга — между ними ходят либо абстрактные, либо очень простые типы данных. Предлагается это свойство сохранить, и попытаться уменьшить связность.


Поставив себя сразу в очень удобное от критики положение, тем что я здесь алгоритмами не занимаюсь, а только дизайном, G по сути дела прыгнул с головой в смоляную яму – отличная позиция. Полностью конечно отрешиться от алгоритмов не удалось, появилась какая-то жуткая мистика про живые процессы, которые должны быть мало связанны и не должны что знать друг про друга, а в следующем мастер классе можно будет сказать, что они наоборот теперь жестко связанны и все друг про друга знают. Конечно от геморроя это никак не избавит, а напротив усугубит еще в большей степени чем раньше. Это я могу гарантировать.
Поэтому еще так слаба пояснительная часть написанная псевдокодами, а чего пояснять –то?
Чистописание кода понравилось, я так не умею. Но сам язык беден.
Re[4]: Код expression, sheet, cell: задачка
От: Cyberax Марс  
Дата: 08.11.07 07:27
Оценка: +1
Здравствуйте, sergesokolov, Вы писали:

S>Поэтому еще так слаба пояснительная часть написанная псевдокодами, а чего пояснять –то?

S>Чистописание кода понравилось, я так не умею. Но сам язык беден.
Так ведь все просто: предложи лучшее решение
Sapienti sat!
Re[5]: Код expression, sheet, cell: задачка
От: Трурль  
Дата: 08.11.07 07:46
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Можно обойтись и простым списком значений — без dictionary. Тогда реально все вытягивается в трубу. Ты имеешь в виду вот это?


G>
G>-module( sheet ).

G>evaluate( Ref, Sheet ) ->
G>   Value = get_cell( Ref, Sheet ), % цыклы не ловим, ломает. Понятно, как, но лень
G>   { Context, NewSheet }= mapfoldl( fun evaluate/2, Sheet, cell:dependency_list( Value ) ),
G>   NewValue = cell:evaluate( Value, Context ),
G>   { NewValue, set_cell( Ref, NewValue ) }.
G>...
G>

Да, примерно это.

Но при случае можно разделить анализ зависимостей и собстванно вычисление.
evaluate( Ref, Sheet ) ->
   Value = get_cell( Ref, Sheet ),
   Context = lists:map( fun ( R ) -> get_cell( R, Sheet ) end, cell:dependency_list(Value) ),
   cell:evaluate( Value, Context ).

evaluate( Sheet ) ->
  DepGraph = build_depgraph(Sheet), 
  case digraph_utils:is_acyclic(DepGraph) of
  true ->  
      RefList = digraph_utils:topsort(DepGraph),
      lists:foldl( fun evaluate/2, Sheet, RefList);
  false -> 
      ...
  end.

Не факт, что так будет лучше, но а вдруг?
Re[6]: Код expression, sheet, cell: задачка
От: red75  
Дата: 08.11.07 08:36
Оценка:
Здравствуйте, Трурль, Вы писали:

T>
Т>evaluate( Sheet ) ->
Т>  DepGraph = build_depgraph(Sheet), 
Т>  case digraph_utils:is_acyclic(DepGraph) of
Т>  true ->  
Т>      RefList = digraph_utils:topsort(DepGraph),
Т>      lists:foldl( fun evaluate/2, Sheet, RefList);
Т>  false -> 
Т>      ...
Т>  end.
Т>


Вместо
RefList = digraph_utils:topsort(DepGraph)

наверное лучше
RefList = lists:reverse(digraph_utils:topsort(DepGraph))

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

Кроме того надо будет ввести cell:make_error() для отметки зацикленных ячеек. А циклы удалить, кажется, просто
evaluate(Sheet)->
  ...
  false ->
    CycPart=digraph_utils:reaching(lists:append(digraph_utils:cyclic_strong_components(DepGraph))),
    Sheet1=lists:foldl(fun(Ref,Sheet)->set_cell(Ref,cell:make_error('#cycle'),Sheet) end,Sheet,CycPart),
    digraph:del_vertices(DepGraph,CycPart),
      ...


Хм. Довольно тяжеловесно получается. И вычислительную сложность нужно прикинуть... Вроде-бы O(N)?
Re[9]: Объяснение устройства cell
От: red75  
Дата: 08.11.07 08:37
Оценка:
Здравствуйте, Курилка, Вы писали:

К>А вообще, интересно есть какие-либо исследования/книги посвящённые этой теме? Что-нибудь с обобщённой точки зрения на вопрос, а не конретика по языкам и т.п.?


Да я в общем-то ненастоящий сварщик. Поэтому советовать остерегусь.
Re[5]: Код expression, sheet, cell: задачка
От: sergesokolov Россия http://www.ideashag.spb.ru
Дата: 08.11.07 09:46
Оценка: :)
Здравствуйте, Cyberax, Вы писали:

C>Здравствуйте, sergesokolov, Вы писали:


S>>Поэтому еще так слаба пояснительная часть написанная псевдокодами, а чего пояснять –то?

S>>Чистописание кода понравилось, я так не умею. Но сам язык беден.
C>Так ведь все просто: предложи лучшее решение
решение чего?
мистических вещей, тут одна словестная ловушка в той терминологии которую выдумавает G.
Бьен Страуструп тоже был в определенной степени мистик со скрытием данных, но у него была четкая цель, которой здесь сознательно нет.
Re[6]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 08.11.07 10:04
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Здравствуйте, Gaperton, Вы писали:


G>>Можно обойтись и простым списком значений — без dictionary. Тогда реально все вытягивается в трубу. Ты имеешь в виду вот это?


Т>Да, примерно это.


Т>Но при случае можно разделить анализ зависимостей и собстванно вычисление.

Т>
Т>evaluate( Ref, Sheet ) ->
Т>   Value = get_cell( Ref, Sheet ),
Т>   Context = lists:map( fun ( R ) -> get_cell( R, Sheet ) end, cell:dependency_list(Value) ),
Т>   cell:evaluate( Value, Context ).

Т>evaluate( Sheet ) ->
Т>  DepGraph = build_depgraph(Sheet), 
Т>  case digraph_utils:is_acyclic(DepGraph) of
Т>  true ->  
Т>      RefList = digraph_utils:topsort(DepGraph),
Т>      lists:foldl( fun evaluate/2, Sheet, RefList);
Т>  false -> 
Т>      ...
Т>  end.
Т>

Т>Не факт, что так будет лучше, но а вдруг?

Вряд ли анализ графа оправдан в такой задаче. В чем преимущество? Цыкл — так его поймать можно и попроще. Первый способ — "раскраска" клеток (когда мы берем вычисление значения в "скобки" считаем/несчитаем — как это в моем старом коде сделано и как нормальные люди делают в плюсах), второй способ — передавать "стек" вычислений параметром (это будет set из ref-ов), и проверять, нет ли повторного захода.

"Раскраска" — это одна накладная операция вставки в sheet значения updating при входе в функцию evaluate.
В случае "стека" — две операции вставки и удаления, зато в меньший по размеру set. Я выбрал "раскраску" потому, что дополнительного контейнера таскать а собой не надо. Красивее, штоли. Собственно, код увеличивается на 3 строки.

G>>
G>>-module( sheet ).

G>>evaluate( Ref, Sheet ) ->
G>>   case get_cell( Ref, Sheet ) of
         updating -> { { error, '#cycle' }, Sheet };
         Value ->
             Sheet_1 = set_cell( Ref, updating, Sheet ),
G>>          { Context, NewSheet }= mapfoldl( fun evaluate/2, Sheet_1, cell:dependency_list( Value ) ),
G>>          NewValue = cell:evaluate( Value, Context ),
G>>          { NewValue, set_cell( Ref, NewValue ) }
      end.
G>>...
G>>


Это еще порефакторить можно, чтобы было красиво. Кстати, вот еще одна лажа. Для ошибки надо делать конструктор. А создается она зараза везде. Во всех модулях.

А вообще — все это частности. Твой подход рулит со страшной силой. Так делать (а именно — заставить клетку знать список ячеек, от которых она зависит) — правильно. Именно это решение ключевое — только этот подход позволяет полностью изолировать алгоритм вычисления шыта от вычисления выражений (у меня это было в куче, и меня это беспокоило). И это решение — безусловно решение дизайна, а не алгоритма. Короче, "я всегда жалел, Штирлиц, что вы работаете не в моем департаменте" .
__________________
Кстати, хаскелисты жгут. potan у себя в ЖЖ опубликовал жосткое решение этой задачи на Хаскеле с использованием "релятивистских эффектов". Там клетки с формулами напрямую ленивыми связями на аргументы замыкаются, а потом вычисляются. Жесть.
Re[4]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 08.11.07 10:31
Оценка:
Здравствуйте, sergesokolov, Вы писали:

G>>Кстати, приглашаю поиграть в "пятнашки". Помоги мне, попробуй улучшить дизайн. Принципы дизайна, которые надо сохранить — очень простые: если ты посмотришь внимательно на код, то ты обратишь внимание, что модули не знают про структуры данных друг друга — между ними ходят либо абстрактные, либо очень простые типы данных. Предлагается это свойство сохранить, и попытаться уменьшить связность.


S>Поставив себя сразу в очень удобное от критики положение, тем что я здесь алгоритмами не занимаюсь, а только дизайном, G по сути дела прыгнул с головой в смоляную яму – отличная позиция. Полностью конечно отрешиться от алгоритмов не удалось, появилась какая-то жуткая мистика про живые процессы, которые должны быть мало связанны и не должны что знать друг про друга, а в следующем мастер классе можно будет сказать, что они наоборот теперь жестко связанны и все друг про друга знают. Конечно от геморроя это никак не избавит, а напротив усугубит еще в большей степени чем раньше. Это я могу гарантировать.

S>Поэтому еще так слаба пояснительная часть написанная псевдокодами, а чего пояснять –то?
S>Чистописание кода понравилось, я так не умею. Но сам язык беден.

Симметричный ответ: Вы, любезный, поставили себя в очень удобное для критики положение. По сути — вы нырнули с головой просто в яму какую-то с чем-то, не скажу с чем. Сами посудите — чистый код вы писать не умеете, проблем дизайна не понимаете, понятие "связности" для вас мистика. И единственное, что вы можете гарантировать — это геморрой. Прям хоть в резюме включай .

Несимметричный ответ: Короче, в этом топике обсуждается проблема и подходы к дизайну для ФЯ. Не мое чистописание, не бедность языков, не алгоритмы (алгоритмы в этой задаче тривиальны — не о чем тут говорить). На примере простой задачи. По существу вопроса есть что сказать? Считаешь, что само понятие дизайна в контексте ФП — глупость? Да ради бога — раскрой тему, с тобой поговорят. Может быть. А вые..ся и переходить на личности здесь не надо.
Re[6]: Условие задачи Problem K
От: Gaperton http://gaperton.livejournal.com
Дата: 08.11.07 10:48
Оценка:
Здравствуйте, sergesokolov, Вы писали:

C>>Так ведь все просто: предложи лучшее решение

S>решение чего?
S>мистических вещей, тут одна словестная ловушка в той терминологии которую выдумавает G.
S>Бьен Страуструп тоже был в определенной степени мистик со скрытием данных, но у него была четкая цель, которой здесь сознательно нет.

Дурачком прикидываться не надо, ок? Вот реальная тестовая задача, которая применялась при найме в реальную компанию — CQG, ее до тебя уже успешно решили уже сотни людей. Так что не надо делать трагическое выражение лица, вставать в позу, и вещать драматическим голосом, работая на публику. Не оценят, посмеются только. Собственно, уже.

http://thesz.livejournal.com/280784.html

Маленький Эксель
----------------

Необходимо реализовать простую электронную таблицу в виде программы, выполняющейся
из командной строки. Она должна уметь обрабатывать ячейки таблицы как и более
продвинутые аналоги, только с упрощенным синтаксисом выражений. Каждая ячейка
может содержать:
— Ничего
— Неотрицательное целое число
— Текстовые строки, которые начинаются с символа '
— Строки-выражения, которые начинаются с символа '=' и могут содержать
неотрицательные целые числа, ссылки на ячейки и простые арифметические
выражения. Скобки запрещены, у всех операций одинаковый приоритет.
Ссылки на ячейки состоят из одной латинской буквы и следующей за ней
цифры.

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

expression ::= '=' term {operation term}*
term ::= cell_reference | nonnegative_number
cell_reference ::= [A-Za-z][0-9] --
operation ::= '+' | '-' | '*' | '/'
text ::= '\'' {printable_character}

Процесс обработки:
— Все выражения должны быть заменены на вычисленный результат.
— Все вычисления выполняются с помощью целочисленной арифметики со знаком.
— Ячейки с текстом должны быть вычислены как соответствующий текст без
префикса '.
— Операции над строками текста запрещены.
— В случае любой ошибки вычисления формулы, вычисляемая ячейка должна содержать
слово-сообщение об ошибке, начинающееся с символа '#'. Используйте короткие,
ясные сообщения. Не надо предоставлять подробности об ошибках в выводе.

Программа должна использовать только стандартные библиотеки и классы и не должна
вызывать сторонние программы, библиотеки или системные компоненты.


Ввод и вывод
------------

Программа получает описание таблицы с формулами из стандартного ввода,
вычисляет ее и печатает полученный результат в стандартный вывод. Входные
данные представлены таблицей, элементы строк которой разделены табуляциями.
Первая строка содержит пару чисел, разделенных табуляцией — высоту и
ширину таблицы, соответственно. Затем идут строки с ячейками таблицы,
в грамматике, приведенной выше.


Выход должен содержать только ожидаемую информацию, включая сообщения об
ошибках, и никакой другой информации в выводе не должно быть, включая и
welcome text. Выход должен быть отформатирован в соответствии с приведенным
ниже примером.

Пример данных:
3 4
12 =C2 3 'Sample
=A1+B1*C1/5 =A2*B1 =B3-C3 'Spread
'Test =4-3 5 'Sheet

Ожидаемый вывод:
12 -4 3 Sample
4 -16 -4 Spread
Test 1 5 Sheet


Указания по решению
-------------------
Необходимо промышленное качество кода. Более короткое и читаемое решение
предпочтительней. Решение должно содержать тестовые примеры и код, использованные
в процессе создания решения. Не забудьте откомментировать код в ключевых
местах. Код должен быть устойчив к ошибкам.

Представьте, что это требования к первой фазе проекта. Необходимо реализовать
только то, что нужно на этой фазе. Однако, известно, что планируется вторая
фаза, в которой требования будут расширены следующими:
— Расширить формулы операциями над строками,
— Оптимизировать производительность для громадных таблиц.

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

Re[5]: Код expression, sheet, cell: задачка
От: sergesokolov Россия http://www.ideashag.spb.ru
Дата: 08.11.07 11:07
Оценка: :))
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, sergesokolov, Вы писали:


G>>>Кстати, приглашаю поиграть в "пятнашки". Помоги мне, попробуй улучшить дизайн. Принципы дизайна, которые надо сохранить — очень простые: если ты посмотришь внимательно на код, то ты обратишь внимание, что модули не знают про структуры данных друг друга — между ними ходят либо абстрактные, либо очень простые типы данных. Предлагается это свойство сохранить, и попытаться уменьшить связность.


S>>Поставив себя сразу в очень удобное от критики положение, тем что я здесь алгоритмами не занимаюсь, а только дизайном, G по сути дела прыгнул с головой в смоляную яму – отличная позиция. Полностью конечно отрешиться от алгоритмов не удалось, появилась какая-то жуткая мистика про живые процессы, которые должны быть мало связанны и не должны что знать друг про друга, а в следующем мастер классе можно будет сказать, что они наоборот теперь жестко связанны и все друг про друга знают. Конечно от геморроя это никак не избавит, а напротив усугубит еще в большей степени чем раньше. Это я могу гарантировать.

S>>Поэтому еще так слаба пояснительная часть написанная псевдокодами, а чего пояснять –то?
S>>Чистописание кода понравилось, я так не умею. Но сам язык беден.

G>Симметричный ответ: Вы, любезный, поставили себя в очень удобное для критики положение. По сути — вы нырнули с головой просто в яму какую-то с чем-то, не скажу с чем. Сами посудите — чистый код вы писать не умеете, проблем дизайна не понимаете, понятие "связности" для вас мистика. И единственное, что вы можете гарантировать — это геморрой. Прям хоть в резюме включай .


G>Несимметричный ответ: Короче, в этом топике обсуждается проблема и подходы к дизайну для ФЯ. Не мое чистописание, не бедность языков, не алгоритмы (алгоритмы в этой задаче тривиальны — не о чем тут говорить). На примере простой задачи. По существу вопроса есть что сказать? Считаешь, что само понятие дизайна в контексте ФП — глупость? Да ради бога — раскрой тему, с тобой поговорят. Может быть. А вые..ся и переходить на личности здесь не надо.


Если Вы обиделись на Яму так это не я выдумал, а это классический труд
http://webkomora.com.ua/ru/articles/web/management/man-month/18.html
вы конечно же его читали, но сгоряча не смогли вспомнить.
Экзамен я у Вас сдавать не собираюсь, потому что семь лет назад создал лучшее в мире решение ввода Пирамидальную Клавиатуру ни японцы ни штитники пока еще ничего не могут этому противополстивить. Я думаю вы тоже когда нибудь что-нибудь сможете, если только будете к себе достаточно критично.
програмирование каждомы нужно для решение своих нужд. Моя цель сейсас создать семантику распознания жеста, Марковские модели для этого не очень годятся, впрочем если у вас другое мнение на эту тему выслушаю с большим удовольствием любую конструктивную критику.
с Огромным уважением
Re[4]: Еще один паттерн проектирования.
От: Gaperton http://gaperton.livejournal.com
Дата: 08.11.07 11:41
Оценка:
Здравствуйте, Трурль, Вы писали:

Т>Каюсь, первая мысль была передавать в expression:evaluate функцию-разыменователь

Т>
Т>evaluate(expression(), Ref->Value ) -> Value.
Т>

Т>Это, конечно, плохо – нельзя исключить перевычислений и к тому же ни разу не идиоматически.

Поговорил с potan-ом, он развил и эту мысль. Можно строить весь дизайн на closures, делая на них иммутабельные объекты, как делают в scheme. Собственно, не фокус, это давно известный трюк, мы его обсуждали тут год-два назад. Скажем, можно передавать в expression вот такую штуку:

sheet_obj( Sheet ) -> fun
   ( get, Ref ] ) ->
       get_cell( Ref, Sheet );
   ( set, { Ref, Value } ) ->
       sheet_obj( set_cell( Ref, Value, Sheet ) );
   ( return, _ ) ->
       Sheet
end.

И тогда

%% evaluate( Ref, Expression, Sheet ) -> { Value, UpdatedSheet }.
%% evaluate expression...
evaluate( _, updating, Sheet ) -> { { error, '#cycle' }, Sheet };
evaluate( Ref, Expr, Sheet ) ->
    Sheet_1 = Sheet( set, { Ref, updating } ),
    { Value, Sheet_2 } = evaluate( Expr, Sheet_1 ),
    { Value, Sheet( set, { Ref, Value } ) }.


Вот так. Такой вот дизайн-паттерн. Я не говорю, что так делать надо в данном случае — но это просто прикольный паттерн, так сказать, про который не надо забывать, что так можно.

Как по разному оказывается можно-то. Короче — поле для деятельности большое. У меня, к сожалению, время не скоро появится — кому-нибудь кроме меня интересно сделать "идеоматический" дизайн на closures-ах? Посмотреть хочется, что получится.
Re[6]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 08.11.07 12:09
Оценка:
Здравствуйте, sergesokolov, Вы писали:

S>програмирование каждомы нужно для решение своих нужд. Моя цель сейсас создать семантику распознания жеста, Марковские модели для этого не очень годятся, впрочем если у вас другое мнение на эту тему выслушаю с большим удовольствием любую конструктивную критику.


Должен вас разочаровать — мне совершенно не интересна эта тема. Я вам настоятельно рекомендую удовлетворять свои нужды в специально отведенных для этого местах, а не в профильных профессиональных форумах. Для замеров пиписьками, самоутверждения, наездов, и переливания из пустого в порожнее есть "священные войны". Для создания пирамидальных клавиатур — "коллеги улыбнитесь".
Re[6]: Код expression, sheet, cell: задачка
От: Cyberax Марс  
Дата: 08.11.07 12:15
Оценка:
Здравствуйте, sergesokolov, Вы писали:

C>>Так ведь все просто: предложи лучшее решение

S>решение чего?
Этот тред — о решении некоторой задачи (см. в первое сообщение).

S>мистических вещей, тут одна словестная ловушка в той терминологии которую выдумавает G.

S>Бьен Страуструп тоже был в определенной степени мистик со скрытием данных, но у него была четкая цель, которой здесь сознательно нет.
С этим в флеймовые форумы, пожалуйста.
Sapienti sat!
Re[7]: Код expression, sheet, cell: задачка
От: sergesokolov Россия http://www.ideashag.spb.ru
Дата: 08.11.07 12:19
Оценка: -3
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, sergesokolov, Вы писали:


S>>програмирование каждомы нужно для решение своих нужд. Моя цель сейсас создать семантику распознания жеста, Марковские модели для этого не очень годятся, впрочем если у вас другое мнение на эту тему выслушаю с большим удовольствием любую конструктивную критику.


G>Должен вас разочаровать — мне совершенно не интересна эта тема. Я вам настоятельно рекомендую удовлетворять свои нужды в специально отведенных для этого местах, а не в профильных профессиональных форумах. Для замеров пиписьками, самоутверждения, наездов, и переливания из пустого в порожнее есть "священные войны". Для создания пирамидальных клавиатур — "коллеги улыбнитесь".

конечно не все на rsdn такие как этот самоуверенный осел
Re[5]: Еще один паттерн проектирования.
От: Cyberax Марс  
Дата: 08.11.07 12:22
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Как по разному оказывается можно-то. Короче — поле для деятельности большое. У меня, к сожалению, время не скоро появится — кому-нибудь кроме меня интересно сделать "идеоматический" дизайн на closures-ах? Посмотреть хочется, что получится.

Я попробую — даже самому интересно стало
Sapienti sat!
Re[8]: Код expression, sheet, cell: задачка
От: Cyberax Марс  
Дата: 08.11.07 13:04
Оценка:
Здравствуйте, sergesokolov, Вы писали:

G>>Должен вас разочаровать — мне совершенно не интересна эта тема. Я вам настоятельно рекомендую удовлетворять свои нужды в специально отведенных для этого местах, а не в профильных профессиональных форумах. Для замеров пиписьками, самоутверждения, наездов, и переливания из пустого в порожнее есть "священные войны". Для создания пирамидальных клавиатур — "коллеги улыбнитесь".

S>конечно не все на rsdn такие как этот самоуверенный осел
Тут недавно Gaperton подрался с примерно таким же персонажем на форуме "Менеджмент и управление проектами". Было очень интересно наблюдать — за персонажем. Чувствую, что в этот раз будет еще веселее

2Модератор: эту ветку пора в СВ, ИМХО.
Sapienti sat!
Re[8]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 08.11.07 13:05
Оценка:
Здравствуйте, sergesokolov, Вы писали:

G>>Должен вас разочаровать — мне совершенно не интересна эта тема. Я вам настоятельно рекомендую удовлетворять свои нужды в специально отведенных для этого местах, а не в профильных профессиональных форумах. Для замеров пиписьками, самоутверждения, наездов, и переливания из пустого в порожнее есть "священные войны". Для создания пирамидальных клавиатур — "коллеги улыбнитесь".

S>конечно не все на rsdn такие как этот самоуверенный осел

Ой, смотрите-ка, мы обиделись. Чего ты ждал, интересно, в ответ на свои хамские выходки и оффтоп?
Re[9]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 08.11.07 14:05
Оценка: +1 :)
Здравствуйте, Cyberax, Вы писали:

C>Тут недавно Gaperton подрался с примерно таким же персонажем на форуме "Менеджмент и управление проектами". Было очень интересно наблюдать — за персонажем. Чувствую, что в этот раз будет еще веселее


Не, не будет. Я не буду отвечать.

А персонаж реально похож. В тот раз, кстати, сначала не забанили никого, потому что типа пришлось бы банить всех сразу — поэтому просто удалили ветку и сказали "мальчики не деритесь". После чего тот персонаж обрадовался и немедленно потребовал от всей конфы извинений — этого уже было малехо через край, и на волне народного гнева награда нашла своего героя .
Re[9]: Код expression, sheet, cell: задачка
От: sergesokolov Россия http://www.ideashag.spb.ru
Дата: 08.11.07 16:54
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, sergesokolov, Вы писали:


G>>>Должен вас разочаровать — мне совершенно не интересна эта тема. Я вам настоятельно рекомендую удовлетворять свои нужды в специально отведенных для этого местах, а не в профильных профессиональных форумах. Для замеров пиписьками, самоутверждения, наездов, и переливания из пустого в порожнее есть "священные войны". Для создания пирамидальных клавиатур — "коллеги улыбнитесь".

S>>конечно не все на rsdn такие как этот самоуверенный осел

G>Ой, смотрите-ка, мы обиделись. Чего ты ждал, интересно, в ответ на свои хамские выходки и оффтоп?


Да я извиняюсь. Мне кстати в вашей работе многое нравится, есть чему поучиться и конечно вам надо ее продолжать. То что сказал не берите близко, тем более, что вы мне ответили и я не в обиде. Последняя фраза была конечно лишней, сказал в запальчивости. Из разговора вышел, что бы не делать удовольствия Cyberax . Придется бедняге сегодня пускать сопли счастья по другому поводу.
Re[7]: Код expression, sheet, cell: задачка
От: Трурль  
Дата: 09.11.07 05:58
Оценка:
Здравствуйте, red75, Вы писали:


R>Вместо

R>
R>RefList = digraph_utils:topsort(DepGraph)
R>

R>наверное лучше
R>
R>RefList = lists:reverse(digraph_utils:topsort(DepGraph))
R>

R>Чтобы не перевычислять одно и тоже по нескольку раз.

Не "наверное лучше", а "единственно правильно". Оно не будет перевычислять, а просто сломается. Я перепутал порядок, в котором topsort возвращет значения.
Re: Не про дизайн
От: Трурль  
Дата: 09.11.07 06:49
Оценка:
Просто занятное наблюдение.
Посмотрел решения, которые опубликовал thesz. Все аккуратно обрабатывают деление на ноль, и никто нипарицца с переполнением.
Допустим, на языках с бесконечными целыми это неактуально. Но сам thesz использует Int, а не Integer.
Re[8]: Код expression, sheet, cell: задачка
От: red75  
Дата: 09.11.07 09:28
Оценка:
Здравствуйте, Трурль, Вы писали:

R>>Вместо

R>>
R>>RefList = digraph_utils:topsort(DepGraph)
R>>

R>>наверное лучше
R>>
R>>RefList = lists:reverse(digraph_utils:topsort(DepGraph))
R>>

R>>Чтобы не перевычислять одно и тоже по нескольку раз.

Т>Не "наверное лучше", а "единственно правильно". Оно не будет перевычислять, а просто сломается. Я перепутал порядок, в котором topsort возвращет значения.


Угу. Я пропустил эту ошибку, потому что предположил, что невычисленные элементы Context, могут быть довычислены в expression:evaluate(), не заметив, что sheet() не передаётся в cell:evaluate(). То есть sheet:evaluate() при таком дизайне становится ключевым элементом, трогать который руками очень не рекомендуется.
Re[9]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 09.11.07 14:07
Оценка:
Здравствуйте, red75, Вы писали:

Т>>Не "наверное лучше", а "единственно правильно". Оно не будет перевычислять, а просто сломается. Я перепутал порядок, в котором topsort возвращет значения.


R>Угу. Я пропустил эту ошибку, потому что предположил, что невычисленные элементы Context, могут быть довычислены в expression:evaluate(), не заметив, что sheet() не передаётся в cell:evaluate(). То есть sheet:evaluate() при таком дизайне становится ключевым элементом, трогать который руками очень не рекомендуется.


Я пришел к выводу, что изначальный дизайн — говно. Я сейчас работаю над новым, с учетом предложений и замечаний твоих, Трурля, Potan-a, и своих собственных. Что это за функциональный дизайн, в котором для инкапсуляции не применяются замыкания? Это какая-то калька с С++/Java/C#, а не дизайн.

Вот сейчас добавим в нужные места замыкания, пробросим где надо exceptions, да воспользуемся подходом Трурля с раздельным запросом зависимостей... Вот тогда посмотрим.
Re[10]: Код expression, sheet, cell: задачка
От: red75  
Дата: 09.11.07 14:48
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Вот сейчас добавим в нужные места замыкания, пробросим где надо exceptions, да воспользуемся подходом Трурля с раздельным запросом зависимостей... Вот тогда посмотрим.


Что-то мне эти зависимости не очень нравятся... А, вот: функция таблицы "=get(Cell)" должна возвращать значение ячейки, указанной в строке/ячейке Cell. Через зависимости не выражается.
Re[11]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 09.11.07 15:01
Оценка:
Здравствуйте, red75, Вы писали:

R>Здравствуйте, Gaperton, Вы писали:


G>>Вот сейчас добавим в нужные места замыкания, пробросим где надо exceptions, да воспользуемся подходом Трурля с раздельным запросом зависимостей... Вот тогда посмотрим.


R>Что-то мне эти зависимости не очень нравятся... А, вот: функция таблицы "=get(Cell)" должна возвращать значение ячейки, указанной в строке/ячейке Cell. Через зависимости не выражается.


Пардон, а что такое в данном случае Cell?
Re[12]: Код expression, sheet, cell: задачка
От: red75  
Дата: 09.11.07 15:15
Оценка:
Здравствуйте, Gaperton, Вы писали:

R>>Что-то мне эти зависимости не очень нравятся... А, вот: функция таблицы "=get(Cell)" должна возвращать значение ячейки, указанной в строке/ячейке Cell. Через зависимости не выражается.


G>Пардон, а что такое в данном случае Cell?


Строка или ячейка. Пример таблицы:
    a          b

1  'b1        'No ref
2  =get("a1") =get(a1)

Вычисленная таблица
    a          b

1  b1         b2
2  b1         No ref
Re[13]: Ошибка в таблице
От: red75  
Дата: 09.11.07 15:17
Оценка:
Вычисленная таблица должна быть
    a          b

1  b1         No ref
2  b1         No ref
Re[13]: Решение №2
От: Gaperton http://gaperton.livejournal.com
Дата: 09.11.07 15:45
Оценка:
Здравствуйте, red75, Вы писали:

R>Здравствуйте, Gaperton, Вы писали:


R>>>Что-то мне эти зависимости не очень нравятся... А, вот: функция таблицы "=get(Cell)" должна возвращать значение ячейки, указанной в строке/ячейке Cell. Через зависимости не выражается.


G>>Пардон, а что такое в данном случае Cell?


R>Строка или ячейка. Пример таблицы:

R>
R>    a          b

R>1  'b1        'No ref
R>2  =get("a1") =get(a1)
R>

R>Вычисленная таблица
R>
R>    a          b

R>1  b1         b2
R>2  b1         No ref
R>


Понятно. А зачем такая во всех смыслах вредная штука нужна? У меня только-только красиво начало получаться. Зацени:

Проблема с рефом:
-module( ref ).
-export( create/1, create/2, row_range/3 ).

%% create( integer(), integer() ) -> ref().
create( Row, Col ) -> { Row, Col }.

%% create( string() ) -> ref() | throws value:error().
create( [ Row, Col ] ) when Row >= $a, Row =< $z, Col >= $0, Col =< $9 ->
    { Row - $a + 1, Col - $0 };
create( _ ) -> value:throw_error( 'invalid ref' ).

%% row_range( Row, From, To ) -> [ ref() ]
%% Row = From = To = integer()
row_range( Row, From, To ) -> [ ref( Row, Col ) || Col <- lists:seq( From, To ) ].


Проблема с value:

-module( value ).
-compile( export_all ).

%% Public data type
%% value() = integer() | string() | error() | empty.
create( [ $', String ] ) -> String;
create( [ $#, String ] ) -> error( list_to_atom( String ) );
create( "" ) -> empty;
create( Int ) -> catch integer( Int ).

%% to_string( value() ) -> string().
%% get text representation for the given cell value
as_string( Int ) when is_integer( Int ) -> integer_to_list( Int );
as_string( { error, Msg } ) -> "#" ++ atom_to_list( Msg );
as_string( empty ) -> "";
as_string( String ) is_list( String ) -> String.

%% error( atom() ) -> error() = { error, Code }
error( Code ) when is_atom( Code ) -> { error, Code }.
throw_error( Msg ) when is_atom( Msg ) -> throw( error( Msg ) ).

%% integer( String ) -> integer() | throw error()
integer( String ) ->
    case string:to_integer( T ) of
        { Int, [] } -> Int;
        { error, no_integer } -> throw_error( 'not a number' ).
    end.

as_integer( empty ) -> 0;
as_integer( Int ) when is_integer( Int ) -> Int;
as_integer( _ ) -> throw_error( 'not a number' ).


А теперь — внимание: операция.

-module( operation ).
-export( [ tokens/0, create/1 ] ).

%% create( char() ) -> Operation
%% Operation = fun( value(), value() ) -> value() | throws error()
%% create binary operator...
create( $+ ) -> fun add/2;
create( $- ) -> fun sub/2;
create( $* ) -> fun mult/2;
create( $/ ) -> fun divd/2.

%% tokens() -> [ char() ].
%% binary operators' token list...
tokens() -> "+-*/".

%% Binary operations implementation... 
%%______________________________________
add( X, Y ) when is_list( X ), is_list( Y ) -> X ++ Y;
add( X, Y ) -> value:as_number( X ) + value:as_number( Y ).

mult( X, Y ) -> value:as_number( X ) * value:as_number( Y ).

sub( X, Y ) -> value:as_number( X ) - value:as_number( Y ).

divd( X, Y ) ->
    X_1 = value:as_number( X ),
    Y_1 = case value:as_number( Y ) of
        0 -> value:throw_error( 'division by zero' );
        Y -> Y
    end,
    X_1 / Y_1.


А теперь — полный ахтунг. Выражение.

-module( expression ).
-export( [ create_expr/1 ] ).

%% create_expr( string() ) -> { Expression, [ ref() ] } | value().
%% Expression = fun( [ value() ] ) -> value()
create_expr( String ) -> catch
        { Expr, Refs } = create( String ),
        F = fun( Args ) -> catch
            { Res, [] } = evaluate( Expr, Args ),
            Res
        end,
        { F, Refs }

%% create( string() ) -> { expression(), refs() }.
%% create expression from text...        
create( Expr ) ->
    Pos = string:cspan( Expr, operation:tokens() ),
    case lists:split( Pos, Expr ) of
        { Val, [ Op | Rest ] } ->
            L = create_arg( Val ),
            { R, Refs } = create( Rest ),
            Node = { L, operation:create( Op ), R },
            { Node, update_refs( L, Refs ) };
        { Val, [] } ->
            H = create_arg( Val ),
            { H, update_refs( H, [] ) }
    end.

update_refs( { ref, Ref }, Refs ) -> [ Ref | Refs ];
update_refs( _, Refs ) -> Refs. 

%% create_arg( string() ) -> Ref | integer()
%% Ref = { integer(), integer() }
create_arg( Arg ) ->
    case catch value:integer( Arg ) of
        { error, _ } -> ref:create( Arg );
        Number -> Number
    end.

%% evaluate( Expression, Args ) -> { Value, [] }.
%% Evaluate expression.
evaluate( { A, Op, B }, Args ) ->
    { ResA, Args_1 } = evaluate( A, Args ),
    { ResB, Args_2 } = evaluate( B, Args_1 ),
    { Op( ResA, ResB ), Args_2 };
evaluate( { ref, _ }, [ Arg | Rest ] ) -> { Arg, Rest };
evaluate( Val, Args ) -> { Val, Args }.


А теперь — cell. Обрати внимание, как просто и симпатично.

-module( cell ).
-export( [    create/1, evaluate/2 ] ).

%% create( String ) -> value() | Formula
%% Formula = { expression(), [ ref() ] }
%% create cell from text representation
create( [ $= | Expr ] ) -> expression:create( Expr );
create( Value ) -> value:create( Value ).

%% evaluate( ref(), sheet() ) -> sheet().
evaluate( Ref, Sheet ) ->
    { _, Sheet_1 } = eval_and_get( Ref, Sheet ),
    Sheet_1.

%% eval_and_get( ref(), sheet() ) -> { value(), sheet() }.
eval_and_get( Ref, Sheet ) ->
    case sheet:get_cell( Ref, Sheet ) of
        updating -> { value:error( 'cyclic reference' ), Sheet };
        { Expr, Refs } ->
            Sheet_1 = sheet:set_cell( Ref, updating, Sheet ),
            { Args, Sheet_2 } = lists:mapfoldl( fun eval_and_get/2, Sheet_1, Refs ),
            Value = Expr( Args ),
            { Value, sheet:set_cell( Ref, Value ) };
        Value -> { Value, Sheet }
    end.


Ну, и наконец, наш факен шыт.

-module( sheet ).
-export( [    new/0, set_cell/3, get_cell/2, 
            update_row/3, get_row/3, create_cell/2, cell_as_string/2, evaluate/1 ] ).

%% new() -> sheet()
%% construct new sheet...
new() -> gb_trees:empty().

get_refs() -> gb_trees:keys( Sheet ).

%% set_cell( ref(), cell(), sheet() ) -> sheet().
set_cell( _, empty, Sheet ) -> Sheet; %%% do NOT store empty values...
set_cell( Ref, Value, Sheet ) -> gb_trees:enter( Ref, Value, Sheet ).

%% set_cell( { ref(), cell() }, sheet() ) -> sheet().
set_cell( { Ref, Value }, Sheet ) -> set_cell( Ref, Value, Sheet ).

%% get_cell( ref(), sheet() ) -> Value.
get_cell( Ref, Sheet ) -> 
    case gb_trees:lookup( Ref, Sheet ) of
        { value, Cell } -> Cell;
        _ -> empty
    end.

%% evaluate( sheet() ) -> sheet().
%% evaluate all expressions...
evaluate( Sheet ) -> lists:foldl( fun cell:evaluate/2, Sheet, get_refs() ).

%% update_row( Row, [ string() ], sheet() ) -> sheet()
%% update row 
create_row( Row, TextCells, Sheet ) ->
    Cells = [ cell:create( String ) || String <- TextCells ],
    Refs = ref:row_range( Row, 1, lists:length( Cells ) ),
    lists:foldl( fun set_cell/2, Sheet, lists:zip( Refs, Cells ) ).

%% get_row( Row, Length, sheet() ) -> [ string() ]
row_as_text( Row, Cols, Sheet ) ->
    [ value:as_string( get_cell( Ref, Sheet ) ) || Ref <- ref:row_range( Row, 1, Length ) ].


Предупреждаю — код не отлаживал. Но все в целом правильно.
Re[11]: Код expression, sheet, cell: задачка
От: Трурль  
Дата: 09.11.07 19:39
Оценка:
Здравствуйте, red75, Вы писали:

R>Что-то мне эти зависимости не очень нравятся...

Ну, мне тоже не совсем.

R>А, вот: функция таблицы "=get(Cell)" должна возвращать значение ячейки, указанной в строке/ячейке Cell. Через зависимости не выражается.

А если представить что будет делать функция "=put(Cell;Value)"...
Re[12]: Код expression, sheet, cell: задачка
От: Gaperton http://gaperton.livejournal.com
Дата: 13.11.07 13:46
Оценка:
Здравствуйте, Трурль, Вы писали:

R>>Что-то мне эти зависимости не очень нравятся...

Т>Ну, мне тоже не совсем.

Наоборот — зависимости в явном виде это круто. Это 100% изоляция способа вычисления expression от остальной программы, и полная независимость остальных алгоритмов от формул.

Вот все, что надо знать о формулах в остальной части программы:
create_expr( string() ) -> { Expression, Arguments } | value().
%% Формулы получаются из строк.
%% возвращает либо конкретное значение, если его можно вычислить сразу, либо функцию, его вычисляющую.
%% Expression = fun( [ value() ] ) -> value()
%% Функция, вычисляющая значение. Берет список значений аргументом.
%% Arguments = [ ref() ]
%% Список ссылок на аргументы.
%% Пример использования: Expression( [ calc_argument( X ) || X <- Arguments ) -> value().


И все. Не слишком много, не так ли?

R>>А, вот: функция таблицы "=get(Cell)" должна возвращать значение ячейки, указанной в строке/ячейке Cell. Через зависимости не выражается.

Т>А если представить что будет делать функция "=put(Cell;Value)"...

Приведите пример use case, где эти волшебные функции используются, и нельзя обойтись явными зависимостями. Также, оцените, насколько он частый, и какому проценту пользователей электронных таблиц нужен.

Мне кажется, этот процент изчезающе мал. Даже если вам удастся привести use case.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.