[Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 20.11.07 17:51
Оценка: 35 (2)
Описание проблемы (и решение на Хаскеле) можно найти здесь.

Тоже не удержался и попробовал решить задачу К на Немерле. Полные исходники можно скачать отсюда.

Проектировать начал снизу вверх (кажется Пол Грэхэм говорил, что bottom up это круто).
Сначала опишем, что такое выражение (описал в виде АТД):

    public variant Expr
    {
        | Literal { v  : int;             }
        | String  { s  : string;          }
        | Add     { e1 : Expr; e2 : Expr; }
        | Sub     { e1 : Expr; e2 : Expr; }
        | Mul     { e1 : Expr; e2 : Expr; }
        | Div     { e1 : Expr; e2 : Expr; }
        | Ref     { i  : int;  j  : int;  }

        static public BuildExpr(s : string) : Expr
        {
             <skipped>
        }

        public Eval(sheet : Sheet) : Expr
        {
             <skipped>
        }
    }


Итак, оно может быть создано из строки и вычислено. Eval принимает как параметр указатель на Sheet, т.к. ему нужно будет вычислять другие ячейки Sheet'а. Может было бы правильнее ссылку на Sheet хранить как член данных, но для простоты я передаю его как параметр (иначе нужно все дерево обходить устанавливая эту ссылку).

Теперь опишем, что есть ячейка:

    public variant Cell
    {
        [Accessor(flags = WantSetter)]
        private mutable position : int * int;
        
        [Accessor(flags = WantSetter)]
        private mutable parent : Sheet;
    
        | Expression { e : Expr;   }
        | Number     { n : int;    }
        | Text       { s : string; }
        | Error      { s : string; }
        | Nil

        public Eval() : Cell
        {
             <skipped>
        }

        public override ToString() : string
        {
             <skipped>
        }
    }


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

А теперь наконец опишем, что такое Sheet:

    public class Sheet
    {
        [Accessor] mutable rows    : int;
        [Accessor] mutable columns : int;
        
        [Accessor(flags = WantSetter)]
        mutable marks : Hashtable[(int * int), bool];
        
        mutable sheet : array [2, Cell];
    
        public this(n : int, m : int, a : array [2, Cell])
        {
             <skipped>
        }
        
        public Eval() : void
        {
             <skipped>
        }
        
        public override ToString() : string
        {
             <skipped>
        }
        
        public Item [i : int, j : int] : Cell
        {
            get { sheet[i, j] }
            set { sheet[i, j] = value }
        }
    }


Тоже умеет вычисляться и показывать себя.

Можно скачать, откомпилить и проверить. К сожалению это не студийный проект, все писал в емаксе. Специально нарушил условия, вместо стандартных потоков использовал файлы, для простоты тестирования.
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re: [Nemerle] Problem K
От: BulatZiganshin  
Дата: 20.11.07 18:00
Оценка: -1 :)
Здравствуйте, Kisloid, Вы писали:

K>Тоже не удержался и попробовал решить задачу К на Немерле.


дурной пример заразителен
Люди, я люблю вас! Будьте бдительны!!!
Re: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 20.11.07 18:06
Оценка:
Таки думаю, как то совсем ненаглядно, лучше покажу полный исходный код:

Expression.n
    public variant Expr
    {
        | Literal { v  : int;             }
        | String  { s  : string;          }
        | Add     { e1 : Expr; e2 : Expr; }
        | Sub     { e1 : Expr; e2 : Expr; }
        | Mul     { e1 : Expr; e2 : Expr; }
        | Div     { e1 : Expr; e2 : Expr; }
        | Ref     { i  : int;  j  : int;  }

        static public BuildExpr(s : string) : Expr
        {
            def parse(s)
            {
                try
                {
                    if (Char.IsDigit(s[0]))
                        Expr.Literal(Convert.ToInt32(s))
                    else
                    {
                        def i = Convert.ToInt32(s.Substring(1)) - 1;
                        def j = Convert.ToInt32(Char.ToUpper(s[0])) - Convert.ToInt32('A');
                        Expr.Ref(i, j)
                    }
                }
                catch
                { | e => throw ParseException(e) }
            }
            
            def build(tokens, ind)
            {
                if (ind >= 2)
                    match (tokens[ind - 1][0])
                    {
                        | '+' => Expr.Add(build(tokens, ind - 2 ), parse(tokens[ind]))
                        | '-' => Expr.Sub(build(tokens, ind - 2 ), parse(tokens[ind]))
                        | '*' => Expr.Mul(build(tokens, ind - 2 ), parse(tokens[ind]))
                        | '/' => Expr.Div(build(tokens, ind - 2 ), parse(tokens[ind]))
                        | _   => throw ParseException()
                    }
                else
                    parse(tokens[ind])
            }
        
            // preprocessing source string
            mutable str = "";
            s.Iter(fun(c) {
                if (['+', '-', '*', '/'].Contains(c))
                    str += " " + c.ToString() + " " 
                else
                    str += c.ToString()
            });

            def tokens = 
                str.Split(array [' '], StringSplitOptions.RemoveEmptyEntries);
            build(tokens, tokens.Length - 1)
        }
        
        public Eval(sheet : Sheet) : Expr
        {
            def eval(e1, e2, root)
            {
              | (e1 is Literal, e2 is Literal, _ is Add) => Literal(e1.v + e2.v)
              | (e1 is Literal, e2 is Literal, _ is Sub) => Literal(e1.v - e2.v)
              | (e1 is Literal, e2 is Literal, _ is Mul) => Literal(e1.v * e2.v)
              | (e1 is Literal, e2 is Literal, _ is Div) =>
                  when (e2.v == 0)
                      throw EvalException("eval");
                  Literal(e1.v / e2.v)

              | (_, _, _) => throw EvalException("eval")
            }

            match (this)
            {
                | Literal (_) => this
                | String  (_) => this
                | Add     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Sub     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Mul     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Div     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Ref     (i,  j ) => 
                    sheet[i, j] = sheet[i, j].Eval();
                    sheet[i, j].Position = (i, j);
                    match (sheet[i, j])
                    {
                        | Number (n) => Literal(n)
                        | Text   (s) => String(s)
                        | _          => throw EvalException("eval")
                    }
            }
        }
    }





Cell.n
    public variant Cell
    {
        [Accessor(flags = WantSetter)]
        private mutable position : int * int;
        
        [Accessor(flags = WantSetter)]
        private mutable parent : Sheet;
    
        | Expression { e : Expr;   }
        | Number     { n : int;    }
        | Text       { s : string; }
        | Error      { s : string; }
        | Nil

        public Eval() : Cell
        {
            match (this)
            {
                | Expression (e) =>
                    when (parent.Marks.Contains(position))
                      throw EvalException("cycle");
                    parent.Marks[position] = true;

                    match (e.Eval(parent))
                    {
                        | Literal (l) => Cell.Number(l)
                        | String  (s) => Cell.Text(s)
                        | _           => throw EvalException("eval")
                    }

                | _ => this
            }
        }

        public override ToString() : string
        {
            match (this)
            {
                | Expression (e) => e.ToString()
                | Number     (n) => n.ToString()
                | Text       (s) => s
                | Error      (s) => "#" + s
                | Nil        => ""
            }
        }
    }





Sheet.n
    public class Sheet
    {
        [Accessor] mutable rows    : int;
        [Accessor] mutable columns : int;
        
        [Accessor(flags = WantSetter)]
        mutable marks : Hashtable[(int * int), bool];
        
        mutable sheet : array [2, Cell];
    
        public this(n : int, m : int, a : array [2, Cell])
        {
            rows    = n;
            columns = m;
            
            sheet = a;
            for (mutable i = 0; i < rows; i++)
                for (mutable j = 0; j < columns; j++)
                {
                    sheet[i, j].Parent   = this;
                    sheet[i, j].Position = (i, j);
                }
        }
        
        public Eval() : void
        {
            for (mutable i = 0; i < rows; i++)
                for (mutable j = 0; j < columns; j++)
                {
                    try
                    {
                        marks = Hashtable();
                        sheet[i, j] = sheet[i, j].Eval();
                    }
                    catch
                    {
                        | e is EvalException =>
                            sheet[i, j] = Cell.Error(e.Message)

                        | _ => throw
                    }
                }
        }
        
        public override ToString() : string
        {
            mutable res = "";
            for (mutable i = 0; i < rows; i++)
            {
                for (mutable j = 0; j < columns; j++)
                    res += sheet[i, j].ToString() + "\t";
                res += "\n"
            }
            res
        }
        
        public Item [i : int, j : int] : Cell
        {
            get { sheet[i, j] }
            set { sheet[i, j] = value }
        }
    }
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re: [Nemerle] Problem K
От: NotGonnaGetUs  
Дата: 21.11.07 09:56
Оценка: +1
Expr <-> Sheet, Expr <-> Сell, Cell <-> Sheet — меджу всеми парами двунаправленные зависимости.
Особенно примечательно поле marks. Нельзя разобраться в том, как работает проверка на цикл, не разобравшись с каждым из методов вычисления (в Sheet, Сell, Expr)...

Данные дублируются: координты cell хранятся в Sheet и в каждой cell...

Приседание
 sheet[i, j] = sheet[i, j].Eval();
 sheet[i, j].Position = (i, j);

говорит о том, что Expr знает не только об интерфейсе Cell, но и о том, как устроен метод Eval в нём!

Добавление новой функции приведёт к изменению в 6-х местах, причём это будет "ещё один копипаст":
1. | Power     { e1 : Expr; e2 : Expr; }
2. | Power => Expr.Power(build(tokens, ind - 2 ), parse(tokens[ind]))
3. | '^' => Expr.Power(build(tokens, ind - 2 ), parse(tokens[ind]))
4. if (['+', '-', '*', '/', '^'].Contains(c))
5. | (e1 is Literal, e2 is Literal, _ is Power) => Literal(e1.v + e2.v)
6. | Power     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)

...

Задача была про "хороший" дизайн. Разве это хороший дизайн?
Re: [Nemerle] Problem K
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.11.07 11:16
Оценка:
Здравствуйте, Kisloid, Вы писали:

Не понял сложностей в реализации Expr.Eval:
public Eval(sheet : Sheet) : Expr
{
        // Зачем этот метод?
        def eval(e1, e2, root)
        {
            // Зачем эти is-ы?
            | (e1 is Literal, e2 is Literal, _ is Add) => Literal(e1.v + e2.v)
            | (e1 is Literal, e2 is Literal, _ is Sub) => Literal(e1.v - e2.v)
            | (e1 is Literal, e2 is Literal, _ is Mul) => Literal(e1.v * e2.v)
            | (e1 is Literal, e2 is Literal, _ is Div) =>
                    when (e2.v == 0)
                            throw EvalException("eval");
                    Literal(e1.v / e2.v)

            | (_, _, _) => throw EvalException("eval")
        }

        match (this)
        {
                | Literal (_) => this
                | String  (_) => this
                | Add     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Sub     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Mul     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Div     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
                | Ref     (i,  j ) => 
                        sheet[i, j] = sheet[i, j].Eval();
                        sheet[i, j].Position = (i, j);
                        match (sheet[i, j])
                        {
                                | Number (n) => Literal(n)
                                | Text   (s) => String(s)
                                | _          => throw EvalException("eval")
                        }
        }
}

Почему было не написать так:
public Eval(sheet : Sheet) : Expr
{
        def eval(expr)
        {
            match (expr.Eval(sheet))
            {
                | Literal(val) => val
                | _            => throw EvalException("eval: vlalue not literal")
            }
        }

        match (this)
        {
                | Literal | String => this
                | Add (e1, e2)     => Literal(eval(e1) + eval(e2))
                | Sub (e1, e2)     => Literal(eval(e1) - eval(e2))
                | Mul (e1, e2)     => Literal(eval(e1) * eval(e2))
                | Div (e1, e2)     => when (e2.v == 0) throw EvalException("eval: div by zero");
                                      Literal(eval(e1) / eval(e2))
                | Ref     (i,  j ) => 
                        sheet[i, j] = sheet[i, j].Eval();
                        sheet[i, j].Position = (i, j);
                        match (sheet[i, j])
                        {
                            | Number(n) => Literal(n)
                            | Text  (s) => String(s)
                            | _         => throw EvalException("eval")
                        }
        }
}
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [Nemerle] Problem K
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.11.07 11:16
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

....

Добавлю так же, что использование двумерного массива решение крайне не экономичное и приведет к большому оверхэду по памяти на таблицах где имеются ячейки с большим номеро строк и/или колонок. Вместо массива "sheet" нужно использовать хэш-таблицу.

NGG>Задача была про "хороший" дизайн. Разве это хороший дизайн?


Лиха беда начало...

К тоу же по ссылке рассказвается о том, что задача выявляла супер-людей-К просто фактом ее решения. Так что даже не оптимальное решение — это зачет.

В прочем, задача сложна для решения на С++ или Яве, а на языках с паттерн-матчнгом она именно что выявляет инженерные (архитекторские) способности.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 21.11.07 11:41
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Expr <-> Sheet, Expr <-> Сell, Cell <-> Sheet — меджу всеми парами двунаправленные зависимости.

NGG>Особенно примечательно поле marks. Нельзя разобраться в том, как работает проверка на цикл, не разобравшись с каждым из методов вычисления (в Sheet, Сell, Expr)...

Expr -> Sheet: Expr знает только о Sheet, т.к. ему нужно по ходу вычислений менять другие ячейки.
Cell <-> Sheet: все ячейки хранят ссылку на Sheet т.к. если он является выражением, то он нужен для вычисления. Sheet состоит из массива Cell'ов.
Cell -> Expr: ячейка может быть выражением, а может быть просто числом, текстом...

NGG>Данные дублируются: координты cell хранятся в Sheet и в каждой cell...


Координаты Cell не хранятся в Sheet, Cell'ы могли быть представлены, списком, мапом итд. Cell'У нужно знать свои координаты, для обнаружения циклов, а координаты не доступны из Sheet'а.

NGG>Приседание

NGG>
NGG> sheet[i, j] = sheet[i, j].Eval();
NGG> sheet[i, j].Position = (i, j);
NGG>

NGG>говорит о том, что Expr знает не только об интерфейсе Cell, но и о том, как устроен метод Eval в нём!

Ну тут да, кривость . Только это говорит не о том, как устроен метод Eval. Просто Cell по сути immutable, не может менять свою внутреннюю структуру. Надо пересоздавать его, а при этом теряется Position. Я думаю просто в сеттере надо было копировать Position.

NGG>Добавление новой функции приведёт к изменению в 6-х местах, причём это будет "ещё один копипаст":

NGG>
NGG>1. | Power     { e1 : Expr; e2 : Expr; }
NGG>2. | Power => Expr.Power(build(tokens, ind - 2 ), parse(tokens[ind]))
NGG>3. | '^' => Expr.Power(build(tokens, ind - 2 ), parse(tokens[ind]))
NGG>4. if (['+', '-', '*', '/', '^'].Contains(c))
NGG>5. | (e1 is Literal, e2 is Literal, _ is Power) => Literal(e1.v + e2.v)
NGG>6. | Power     (e1, e2) => eval(e1.Eval(sheet), e2.Eval(sheet), this)
NGG>

NGG>...

Эммм... я думаю тут главное, что это затрагивает только Expr.

NGG>Задача была про "хороший" дизайн. Разве это хороший дизайн?


Честно говоря я не задавался целью хорошего дизайна, цель была сделать как можно проще.
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re[3]: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 21.11.07 11:48
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Добавлю так же, что использование двумерного массива решение крайне не экономичное и приведет к большому оверхэду по памяти на таблицах где имеются ячейки с большим номеро строк и/или колонок. Вместо массива "sheet" нужно использовать хэш-таблицу.


Согласен, просто не задавался целью оптимального решения. Premature optimization типа

NGG>>Задача была про "хороший" дизайн. Разве это хороший дизайн?


VD>Лиха беда начало...


Вот как разгонюсь...

VD>В прочем, задача сложна для решения на С++ или Яве, а на языках с паттерн-матчнгом она именно что выявляет инженерные (архитекторские) способности.


Не покажешь высший пилотаж?
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re[2]: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 21.11.07 11:55
Оценка:
Здравствуйте, VladD2, Вы писали:

Хотел показать как легко и просто можно будет добавить например конкатенацию строк через '+', потом честно гря просто стало в лом...
VD>Не понял сложностей в реализации Expr.Eval:
VD>
VD>public Eval(sheet : Sheet) : Expr
VD>{
VD>        // Зачем этот метод?
VD>        def eval(e1, e2, root)
VD>        {
VD>            // Зачем эти is-ы?
VD>            | (e1 is Literal, e2 is Literal, _ is Add) => Literal(e1.v + e2.v)
               | (e1 is String,  e1 is String,  _ is Add) => String (e1.s + e2.s)
VD>            | (e1 is Literal, e2 is Literal, _ is Sub) => Literal(e1.v - e2.v)
VD>            | (e1 is Literal, e2 is Literal, _ is Mul) => Literal(e1.v * e2.v)
VD>            | (e1 is Literal, e2 is Literal, _ is Div) =>
VD>                    when (e2.v == 0)
VD>                            throw EvalException("eval");
VD>                    Literal(e1.v / e2.v)

VD>            | (_, _, _) => throw EvalException("eval")
VD>        }
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re[2]: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 21.11.07 12:26
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Особенно примечательно поле marks. Нельзя разобраться в том, как работает проверка на цикл, не разобравшись с каждым из методов вычисления (в Sheet, Сell, Expr)...


Про Expr не понял, не надо знать как работает Expr.Eval(), чтобы разобраться в проверке на цикл. А Sheet <-> Cell, Marks является частью внешнего интерфейса Sheet. Cell пользуется Marks, чтобы понять, не зациклился ло он. Предложите, как поступить иначе?

NGG>Задача была про "хороший" дизайн. Разве это хороший дизайн?


Эммм... может покажете, что такое хороший дизайн?
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re[3]: [Nemerle] Problem K
От: BulatZiganshin  
Дата: 21.11.07 12:53
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>К тоу же по ссылке рассказвается о том, что задача выявляла супер-людей-К просто фактом ее решения.


какой-либо попытки проверки решения я здесь не заметил
Люди, я люблю вас! Будьте бдительны!!!
Re[4]: [Nemerle] Problem K
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.11.07 12:53
Оценка:
Здравствуйте, Kisloid, Вы писали:

K>Согласен, просто не задавался целью оптимального решения. Premature optimization типа


Там в условиях сказано:

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


VD>>В прочем, задача сложна для решения на С++ или Яве, а на языках с паттерн-матчнгом она именно что выявляет инженерные (архитекторские) способности.


K>Не покажешь высший пилотаж?


На плюсах то? Не, я лучше гавкну. Меня последние 3 года тошнит когда приходится на этом языке писать. Ощущения такие как будто в наручниках сидишь.

Вот твою версию можно, наверно, на досуге немного причесать и подоптимизировать.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [Nemerle] Problem K
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.11.07 12:53
Оценка:
Здравствуйте, Kisloid, Вы писали:

K>Хотел показать как легко и просто можно будет добавить например конкатенацию строк через '+', потом честно гря просто стало в лом...

VD>>Не понял сложностей в реализации Expr.Eval:
VD>>
VD>>public Eval(sheet : Sheet) : Expr
VD>>{
VD>>        // Зачем этот метод?
VD>>        def eval(e1, e2, root)
VD>>        {
VD>>            // Зачем эти is-ы?
VD>>            | (e1 is Literal, e2 is Literal, _ is Add) => Literal(e1.v + e2.v)
K>               | (e1 is String,  e1 is String,  _ is Add) => String (e1.s + e2.s)
VD>>            | (e1 is Literal, e2 is Literal, _ is Sub) => Literal(e1.v - e2.v)
VD>>            | (e1 is Literal, e2 is Literal, _ is Mul) => Literal(e1.v * e2.v)
VD>>            | (e1 is Literal, e2 is Literal, _ is Div) =>
VD>>                    when (e2.v == 0)
VD>>                            throw EvalException("eval");
VD>>                    Literal(e1.v / e2.v)

VD>>            | (_, _, _) => throw EvalException("eval")
VD>>        }
K>


Ясно...

Но тогда опять же... зачем is? Можно же так:
def eval(e1, e2, root)
{
    // Зачем эти is-ы?
    | (Literal(e1), Literal(e2), Add) => Literal(e1 + e2)
    | (String(e1),  String(e2),  Add) => String (e1 + e2)
    | (Literal(e1), Literal(e2), Sub) => Literal(e1 - e2)
    | (Literal(e1), Literal(e2), Mul) => Literal(e1 * e2)
    | (Literal(e1), Literal(e2), Div) =>
            when (e2 == 0)
                    throw EvalException("eval");
            Literal(e1.v / e2.v)

    | (_, _, _) => throw EvalException("eval")
}
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [Nemerle] Problem K
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.11.07 12:53
Оценка:
Здравствуйте, Kisloid, Вы писали:

K>Expr -> Sheet: Expr знает только о Sheet, т.к. ему нужно по ходу вычислений менять другие ячейки.


Тогда разумно переместить вычисление в Sheet... наверно.

K>Cell <-> Sheet: все ячейки хранят ссылку на Sheet т.к. если он является выражением, то он нужен для вычисления. Sheet состоит из массива Cell'ов.


Опчть же. Если вычисления в Sheet, то и ячейкам ничего знать про себя кроме содержимого не надо. По сути Cell и Expr — это хранилища данных. Ты ведь не даром их вариантами представил?

K>Cell -> Expr: ячейка может быть выражением, а может быть просто числом, текстом...


Ну, это понятно.

NGG>>Данные дублируются: координты cell хранятся в Sheet и в каждой cell...


K>Координаты Cell не хранятся в Sheet, Cell'ы могли быть представлены, списком, мапом итд. Cell'У нужно знать свои координаты, для обнаружения циклов, а координаты не доступны из Sheet'а.


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

Мне еще не понравилось то, что свойства Parent и Parent класса Sheet можно менять извне Sheet-а.

Мне кажется, что по минимуму нужно сделать Sheet вложенным в Cell а сеттеры приватными. В прочем решение явно кривоватое.

Лучше все же перенести вычисления в Sheet, а Cell и Expr сделать чистыми хранилищами данных. В прочем, мне так же не ясно зачем вообще нужен Cell. Возможно разумнее было бы отказаться от него добавив в Expr вхождение позволяющиее хранить сообщение об ошибке.

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

K>Честно говоря я не задавался целью хорошего дизайна, цель была сделать как можно проще.


Ну, получилось, мне кажется, немного сложнее чем могло бы быть. Понятно, что соврешенство всегда оставляет простор для фантазии. Но мне кажется, если ты учтешь замечения, то код будет и проще, и чище.

Кстати, вместо сакроса Acssesor теперь удобнее использовать упрощенный синтаксис объявления свойств. Так твои свойства можно будет объявить следующим образом:
public partial class Sheet
{
    public Rows    : int                          { get; private set; }
    public Columns : int                          { get; private set; }
    public Marks   : Hashtable[(int * int), bool] { get; private set; }
    public Sheet   : array [2, Cell]              { get; private set; }
  ...

И Marks, наверно, не стоит делать полем. Это структура нужна только на время вычисления. Ее следут созоавать в функции вычисления.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [Nemerle] Problem K
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.11.07 12:57
Оценка:
Здравствуйте, Kisloid, Вы писали:

K>Про Expr не понял, не надо знать как работает Expr.Eval(), чтобы разобраться в проверке на цикл. А Sheet <-> Cell, Marks является частью внешнего интерфейса Sheet. Cell пользуется Marks, чтобы понять, не зациклился ло он. Предложите, как поступить иначе?


1. Перенести рассчеты в Sheet.
2. Убрать Marks из списка полей, сделав его параметром.
3. Убрать обратные ссылки из Cell и Expr.
4. (под вопросом) Отказаться от Cell. Оно похоже лишний.

K>Эммм... может покажете, что такое хороший дизайн?


Хороший дизайн всегда легко оценить, но тудно создать. Иными словами трепаться про Хороший дизайн (тм) всегда проще, чем сделать его самому.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [Nemerle] Problem K
От: BulatZiganshin  
Дата: 21.11.07 13:02
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>На плюсах то? Не, я лучше гавкну.


этим ты нас не удивишь

что касается хорошего дизайна — то знания всех обо всём, на мой взгляд, не обязательны. expr для вычисления нужно только передать функцию (интерфейс) "номер ячейки -> значение". sheet должен предоставлять энумератор. операции должны быть представлены лямбдами. и т.д.
Люди, я люблю вас! Будьте бдительны!!!
Re[4]: [Nemerle] Problem K
От: BulatZiganshin  
Дата: 21.11.07 13:04
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Лучше все же перенести вычисления в Sheet


в конечном счёте у тебя выходит, что всю логику надо перенести в sheet и там уж приготовить знатное спагетти
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 21.11.07 13:25
Оценка:
Здравствуйте, VladD2, Вы писали:

K>>Согласен, просто не задавался целью оптимального решения. Premature optimization типа


VD>Там в условиях сказано:

VD> — Оптимизировать производительность для громадных таблиц.

ясно, просто я не обратил на это внимания

VD>>>В прочем, задача сложна для решения на С++ или Яве, а на языках с паттерн-матчнгом она именно что выявляет инженерные (архитекторские) способности.


K>>Не покажешь высший пилотаж?


VD>На плюсах то? Не, я лучше гавкну. Меня последние 3 года тошнит когда приходится на этом языке писать. Ощущения такие как будто в наручниках сидишь.


Ну почему же на плюсах, на Немерле хочется увидеть близкий к идеалу вариант.
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re[4]: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 21.11.07 13:32
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Ясно...


VD>Но тогда опять же... зачем is? Можно же так:

VD>
VD>def eval(e1, e2, root)
VD>{
VD>    // Зачем эти is-ы?
VD>    | (Literal(e1), Literal(e2), Add) => Literal(e1 + e2)
VD>    | (String(e1),  String(e2),  Add) => String (e1 + e2)
<skipped>
VD>


Понятно, просто плохо умею готовить матчи
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Re[4]: [Nemerle] Problem K
От: Kisloid Мухосранск  
Дата: 21.11.07 14:03
Оценка:
Здравствуйте, VladD2, Вы писали:

K>>Expr -> Sheet: Expr знает только о Sheet, т.к. ему нужно по ходу вычислений менять другие ячейки.


VD>Тогда разумно переместить вычисление в Sheet... наверно.


Как раз таки мне кажется, что это совсем не разумно. Тогда я думаю получится каша, все переместится в Sheet. Я пытался выделить минимально зависимые друг от друга отдельные сущности. А так получается, выражению для вычисления грубя говоря нужно знать только свой контекст (контекст в данном случае это Sheet).

K>>Координаты Cell не хранятся в Sheet, Cell'ы могли быть представлены, списком, мапом итд. Cell'У нужно знать свои координаты, для обнаружения циклов, а координаты не доступны из Sheet'а.


VD>Опять же стоит подумать над тем, чтобы перенести логику обнаружения циклов в Sheet.


Можно подумать.

VD>Мне еще не понравилось то, что свойства Parent и Parent класса Sheet можно менять извне Sheet-а.


У Sheet'а нет свойства Parent, он есть у Cell'ов. Они хранят ссылку на тот Sheet, которому они принадлежат. То что их можно менять, да минус, сделано для простоты, иначе надо думать вот над чем. Если Parent передавать в конструкторе, то возникает ситуация, что Cell'ы иногда нужно создавать без Sheet'a. Точнее сначала создаются Cell'ы, потом только Sheet.

VD>Лучше все же перенести вычисления в Sheet, а Cell и Expr сделать чистыми хранилищами данных. В прочем, мне так же не ясно зачем вообще нужен Cell. Возможно разумнее было бы отказаться от него добавив в Expr вхождение позволяющиее хранить сообщение об ошибке.


Как то мне это не нравится. У меня была такая простая мысль, которая первая в голову наверное и пришло: Sheet хранилище Cell'ов, а Cell'ы в свою очередь могут быть либо числом, текстом, ошибкой, пустотой или формулой (да, надо было назвать это формулой, на Expr). А в свою очередь формула (Expr) по сути AST, может быть умножением, сложением, ..., числом, строкой. Все это сразу нарисовалось в виде АТД.

VD>Ну, и наврено более разумно не менять, при вычислении, значение в саомо Sheet, а создавать новый Sheet с вычесленными значениями.


Да я думал над этим, изначально хотел, чтобы все мои структуры были по природе immutable. Только вот по ходу решения для упрощения (наверное на самом деле получилось наоборот) пришлось от этой мысли отказаться.

VD>Кстати, вместо сакроса Acssesor теперь удобнее использовать упрощенный синтаксис объявления свойств. Так твои свойства можно будет объявить следующим образом:

VD>
VD>public partial class Sheet
VD>{
VD>    public Rows    : int                          { get; private set; }
VD>    public Columns : int                          { get; private set; }
VD>    public Marks   : Hashtable[(int * int), bool] { get; private set; }
VD>    public Sheet   : array [2, Cell]              { get; private set; }
VD>  ...
VD>

VD>И Marks, наверно, не стоит делать полем. Это структура нужна только на время вычисления. Ее следут созоавать в функции вычисления.

Ясно, а насчет Marks согласен, сам сначала тоже так хотел сделать, передавать его в Cell.Eval(marks = null) как параметр по умолчанию. При первом вызове при передаче null создать marks и далее уже передавать ссылку на этот marks. Вот.
((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.