singalen: (portrait)
[personal profile] singalen
Хороший человек [livejournal.com profile] vbayda показал, как он использует boost::bind:
if (entityes_.end() == std::find_if(entityes_.begin(), entityes_.end(),
     boost::bind < bool > (&intersect < double >, c,
         boost::bind(&world::positions::entity::shape_,
            boost::bind(&world::entity::positions_, _1)
         )
     )
    ))

Как на меня, мягко выражаясь, выглядит не слишком читабельно.
Понял слова Алана Перлиса "избыток синтаксического сахара приводит к раку фигурных скобок" (переврал цитату, и ну его).
Не слишком-то C++ пригоден для функциональщины. Правда, синтаксис получился не намного хуже, чем у Lisp-а.

Попытался переписать это на Haskell. Не могу скомпилировать, хоть ты тресни. Хотя выглядит немного лучше.
Комментирую синтаксис "в стиле учебника" для тех, кто не знает языка.
module Main
  where

-- это описание типа данных - "записи" с конструктором и одним полем
data Entity = CreateEntity { position :: Position }

data Position = CreatePosition { circle :: Circle }

data Circle = CreateCircle { x, y, r :: Float }

-- указание типа функции (аргументов и параметров).
-- Обычно можно опустить, type inferrance его определяет.
circlesIntersect :: Circle -> Circle -> Bool
-- это собственно описание функции: имя, параметры, =, тело-выражение
circlesIntersect c1 c2 = sqr (r c1 + r c2) < sqr (x c1 - x c2) + sqr (y c1 - y c2)
  where sqr x = x*x

findIntersecting :: Circle -> [Entity] -> Entity
findIntersecting circle entities = 
-- это тот самый байнд одного (первого) параметра. Выглядит так: (имя функции) параметр.
  find ((entityMatches) circle) entities
  where
    entityMatches :: Circle -> Entity -> Bool
    entityMatches c e = circlesIntersect c (circle position e)
-- Оказывается, в Haskell обращение к полям структуры выглядит не как c.x, а как x(c).
-- Или x c, что в этом случае то же самое.

main = putStr "Hello\n"

Итого получается (получилось бы, если бы я лучше понимал синтаксис и типизацию)
findIntersecting circle entities = 
  find ((entityMatches) circle) entities where
    entityMatches c e = circlesIntersect c (circle position e)

или, без промежуточной "объясняющей" функции
findIntersecting circle entities = 
  find (((circlesIntersect) c (circle position e)) circle) entities

Считаем скобки, почти как в Лиспе. Кстати, синтаксис Лиспа должен был быть похож на хаскельный. Но его где-то там не смогли заимплементить и бросили нынешнем виде, который предназначался только для списковых данных.

(no subject)

6/10/06 13:57 (UTC)
Posted by [identity profile] gdy.livejournal.com
>избыток синтаксического сахара приводит к раку фигурных скобок
>Синтаксический сахар вызывает рак точек с запятой (semicolon).
Эта цитата в переводе звучит глупо.
Colon - это и двоеточие, и толстая кишка.

(no subject)

6/10/06 14:27 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
Я предпочитаю такие вот отступы. Те же самые методы борьбы с круглыми скобками, что и с фигурными. Вполне читабельно и не надо ничего "считать":
if 
(
    entities.end() == 
    std::find_if
    (
       entities_.begin(),
       entities.end(),
       boost::bind
       (
           &intersect, 
           c,
           boost::bind
           (
               &world::positions::entity::shape,
               boost::bind
               (
                    &world::entity::positions, 
                    _1
               )
           )
       )
    )
)
Но в Си++ наоборот, недостаток синтаксического сахара вызывает рак угловых скобок. А вообще такие конструкции содержат больше кода, не поддаются отладке и жутко тормозят.

(no subject)

6/10/06 17:20 (UTC)
Posted by [identity profile] vbayda.livejournal.com
1. отступы - позитивные, спасибо:);
2. насчет жутких тормозов - неуверен, лишнеие вызовы - это не жуткие тормоза;
3. отлаживать осбо нечего: брек поинт ты на забинженую функцию всегда можешь поставить, правильно ли ты сбиндил - проверяется во времени компиляции. Единственное что может раздражать при отладке так это call stack:) (например в boost::serialization - он вообще сумашедший ), ну и здесь немаленький будет;

4. да, неплохо было бы что-то вроде лямбды только на уровне языка
и с возможностями побольше чем в boost::lambda :));

(no subject)

6/10/06 19:14 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
2. Лишние операции копирования, трудности с non-copyable
3. Имелась ввиду отладка километровых сообщений об ошибках компиляции с метровыми именами типов
4. Помогли бы локальные анонимные функции:
std::for_each
(
    myvector,
    void ()(const std::string &x)
    {
        puts(x.c_str());
    }
)
Тут заодно я использовал for_each, принимающий в качестве аргумента range (нечто с begin() и end(). range вроде обещают скоро включить в стандарт.

Более того, в рамках можели исполнения C++ ничего не стоит реализовать для локальных функций частичную функциональность closures - позволить локальным функциям ссылаться на внешние переменные на стеке.

Но честно говоря, я бы предпочел что-то вроде nemerle (суперязык под .NET) + barton (компилятор MSIL -> Native из проекта Singularity). C++ слишком разжирел - новые && ссылки это просто бред и хак.

Нужен сверхбыстрый язык без наследия.

(no subject)

7/10/06 17:49 (UTC)
Posted by [identity profile] vbayda.livejournal.com
2. ну тут тоже самое что и с STL алгоритмами и контейнерами. Когда копировнаие невозможно или критично - используются указатели;
3. есть такое, но к этому привыкаешь. А еще гдето встречались скрипты, преобразующиее большой и сложный output с ошибками в нечто более простое, правда они сильно под STL заточены были;

(no subject)

7/10/06 18:32 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
2. Подход с лишними указателями на ровном месте изначально ущербный - лишняя надстройка и лишние выделения памяти.

3. Привыкание ничего не меняет. Люди и при -60, и при +50 градусах живут, но я к такому привыкать не хочу.

С++ - реально калообразный язык. Нужно "что-то массовое, современное", но столь же быстрое. Я фанат скорости С++, а во всем остальном он полный отстой. STL и исключения использовать нельзя почти никогда - они сразу становятся ботлнеком. А если скорость некритична - можно какой язык попроще и погибче выбрать.

(no subject)

6/10/06 21:04 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
С++ мертв. Уже в текущем варианте это перебор. Посмотрите ужас исходников boost::mpl/lambda. Я писал выше - нужен эффективный компилятор в native для того чтобы какой-нибудь современный язык смог полностью вытеснить С++. Мейнстрим безусловно удовлетворит и nemerle в текущем дотнет-варианте, но всегда будут маньяки желающие именно высщую скорость. Я не вижу причин почему нормальный язык не может свести на ноль сегодняшние преимущества ++ в скорости и низкоуровневости.

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

(no subject)

6/10/06 22:02 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
Перлу очень мешает - один из самых нечитабельных языков. Впрочем, лучше чем Си. Ты на Perl 6 смотрел?

Супер-производительность важна для того чтобы умер С++. Иначе этот зомби будет всех мучать и в третьем тысячелетии.

Unmanaged и даже отсутствие gc - не важно. Важна скорость, которой можно добиться и в рамках garbage collected managed x86 (я подразумеваю аналог пока не существующего Microsoft Singularity TAL - обычный код + метаданные обеспечивающие верифицируемость). Ну и конечно нужен очень мощный компилятор и JITter - теоретически функциональный код лучше оптимизируются чем императивный. Сейчас компилятор C# вообще не содержит оптимизатора.

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

Мейнстрим и сам выживет - важно убить ++, Open Source сразу расцветет.

(no subject)

7/10/06 08:49 (UTC)
Posted by [identity profile] migmit.livejournal.com

   
  


  


     { position   }


     { circle   }


     { x, y, r   }


 circlesIntersect      


 circlesIntersect c1 c2  sqr (r c1  r c2)  sqr (x c1  x c2)  sqr (y c1  y c2)
      sqr x  xx


 findIntersecting    []  
 findIntersecting circle_ entities  


(no subject)

7/10/06 08:50 (UTC)
Posted by [identity profile] migmit.livejournal.com
     fromJust  find (entityMatches circle_) entities

         
           entityMatches      
           entityMatches c e  circlesIntersect c (circle  position e)



 main  putStr 


(no subject)

7/10/06 08:51 (UTC)
Posted by [identity profile] migmit.livejournal.com

 findIntersecting circle_ entities  
     fromJust  find (entityMatches circle_) entities 
         entityMatches c e  circlesIntersect c (circle  position e)



 findIntersecting circle_ entities  
     fromJust  find (c e  circlesIntersect c  circle  position e) circle_  entities


 findIntersecting circle_ entries 
     fromJust  find (e  circlesIntersect circle_  circle  position e) entries


 findIntersecting circle_ entries 
     fromJust  find (circlesIntersect circle_  circle  position) entries

(no subject)

7/10/06 18:39 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
А можно полный текст в окончательном виде?

(no subject)

7/10/06 20:56 (UTC)
Posted by [identity profile] migmit.livejournal.com

   
  
  

     { position   }
     { circle   }
     { x, y, r   }

       
  c1 c2  sqr (r c1  r c2)  sqr (x c1  x c2)  sqr (y c1  y c2)
      sqr x  xx

     []  
  circle_ entries 
     fromJust  find (circlesIntersect circle_  circle  position) entries

(no subject)

8/10/06 07:46 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
Я ничего не понимаю в Хаскеле, но может, логичнее было возвращать таки Maybe Entity или вообще не специфицировать тип так как он все равно всегда будет совпадать с типом возврата find?

> findIntersecting :: Circle -> [Entity] -> Maybe Entity
> findIntersecting circle_ entries =
> find (circlesIntersect circle_ . circle . position) entries

Re: Reply to your comment...

8/10/06 09:21 (UTC)
Posted by [identity profile] migmit.livejournal.com
Это зависит от желаний программиста. Хотите - возвращайте Maybe Entity, мне-то что? Написанный код корректен.
Тип, действительно, можно не указывать - он будет выведен автоматически.

Re: Reply to your comment...

8/10/06 11:44 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
Что вернет fromJust если find вернет Nothing?

Что полезного дает спецификация типа? Меняется ли от наличия спецификации в данном случае какое-либо поведение времени компиляции и времени исполнения?

Re: Reply to your comment...

9/10/06 06:06 (UTC)
Posted by [identity profile] migmit.livejournal.com
fromJust Nothing не вернёт ничего, программа вылетит с ошибкой.
Если нужно возвращать что-то конкретное, можно воспользоваться функцией maybe, имеющей тип b -> (a -> b) -> Maybe a -> b - первый аргумент задаёт значение в случае Nothing, второй - функцию, обрабатывающую x в случае Just x. Так, fromJust можно определить как maybe (error "Что-то там ошибочное") id.
Спецификация даёт проверку при компиляции на отсутствие тривиальных ошибок. Точнее, даёт возможность отловить ошибку поближе к тому месту, где она допущена.
Поведение правильно написанной программы от наличия спецификации не меняется.
Иногда ОТСУТСТВИЕ спецификации позволяет обнаружить, что написанная функция более обща, чем казалось.
Posted by [identity profile] nponeccop.livejournal.com
C++ (хоть он является моей основной специализацией) меня раздражает наличием этих самых спецификаций и необходимостью дописывать дополнительные спецификации если хочется обобщить код. В Хаскеле для обобщения требуется наоборот сократить спецификации и упростить код.

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

Вопрос: Чем чреват подход, при котором типы не специфицируются без крайней необходимости? Можно привести какие-то примеры ошибок, легче диагностирующихся при полной спецификации?

Вы имеете ввиду такой пример? Скажем в стеке вызовов функций все функции принимают произвольный тип, но несуществующий интерфейс требует скажем только самая нижняя (самая внутренняя) функция. В этом случае компилятор выдаст ошибку во внутренней функции и стек вызовов придется отслеживать самому. А в случае явной спецификации компилятор укажет на ошибку сразу на самом верху?
Posted by [identity profile] migmit.livejournal.com
nis> В Хаскеле для обобщения требуется наоборот сократить спецификации и упростить код.
Я написал "иногда". В общем случае, нужны более серьёзные телодвижения. Хотя к спецификации типов они имеют весьма опосредованное отношение.
nis> В "скриптовых" языках все не только умудряются жить "без типов", но и даже находят в этом определенные преимущества.
Здесь следует различать. Есть языки со слабой типизацией - например, Perl. Там вполне можно написать 2+"three" и получить более-менее осмысленный результат. ИМХО, такой подход имеет право на существование, если мы пишем очень маленькие скрипты для выполнения небольших задач. Пожалуй, единственное значимое исключение здесь (из известных мне) - Tcl, пригодный для относительно крупных вещей, благодаря некоей объединяющей идее ("всё есть строка"). На самом деле, там с типами тоже не всё так просто, и перловской мешанины там нет.
А есть языки с сильной, но динамической типизацией. Где рассогласование типов вызывает ошибку времени ИСПОЛНЕНИЯ. И вот здесь появляются наиболее мощные вещи - Lisp, SmallTalk, JavaScript (ох... вынужден я сейчас писать на некоем диалекте жабаскрипта - тех, кто его (диалект) придумывал, убить мало; надо ж так кастрировать замечательный язык). Вот они, как раз, пригодны для очень серьёзных проектов.
Я бы сказал так. Языки со статической сильной типизацией (тот же Haskell, например; C++ к ним не относится, там типизация слабая) хороши тогда, когда мы совершенно чётко представляем себе, что мы пишем. Если же проект предполагает некое исследование возможностей, если могут случиться очень серьёзные изменения дизайна - нужен язык с динамической сильной типизацией. Языки же со слабой типизацией нужны для маленьких вещей, где можно обойтись без дизайна вообще и сделать нужную программу на интуиции.
nis> Вы имеете ввиду такой пример? Скажем в стеке вызовов функций все функции принимают произвольный тип, но несуществующий интерфейс требует скажем только самая нижняя (самая внутренняя) функция. В этом случае компилятор выдаст ошибку во внутренней функции и стек вызовов придется отслеживать самому. А в случае явной спецификации компилятор укажет на ошибку сразу на самом верху?
Да, например, вполне возможный сценарий. Именно поэтому в Лиспе, например, предусмотрено объявление типов переменных (хотя и является необязательным).

Re: Reply to your comment...

16/10/06 16:41 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
Думаю лучшим (и простейшим) вариантом будет возвращать как раз то что возвращает Find - Just x в случае успеха и Nothing в случае если пересечение не найдено. Код поиска наличия пересечения, не работающий в случае отсутствия этого пересечения, как-то странновато выглядит. Естественно, бывают случаи когда это определено контрактом, но в типичной ситуации лучше наверное вести себя так же как стандартная find

Re[2]: Reply to your comment...

17/10/06 05:02 (UTC)
Posted by [identity profile] migmit.livejournal.com
Ну, я, в общем, примерно это и имел в виду, говоря "как захочет программист".

(no subject)

9/10/06 08:23 (UTC)
Posted by [identity profile] migmit.livejournal.com
Ещё одно замечание стилистического характера: зачем нужны три разных типа (Entity, Position и Circle), делающие одно и то же?

(no subject)

16/10/06 16:45 (UTC)
Posted by [identity profile] nponeccop.livejournal.com
То есть внешние типы - аналоги структур с одним полем. Выглядят декларации одинаково только потому что в реальности будут еще специфические поля.

(no subject)

9/10/06 06:11 (UTC)
Posted by [identity profile] migmit.livejournal.com
М.б., стоит прочитать хотя бы один с начала и до конца?

(no subject)

22/11/06 20:48 (UTC)
Posted by [identity profile] aleksijb.livejournal.com
Как я понял, из кода на Haskell-е, интересовало именно пересечение кругов, а не контуров.

Пересечение кругов на Smalltalk-е:

circles anySatisfy:
[:e | (c center dist: e center) < c radius + e radius]

Здесь "c" - круг с которым мы ищем пересечения. dist: - вычилсяет растояние между точками.
Ответом будет true либо false. Если надо выбрать только круги пересекающий данный то надо anySatisfy: заменить на select:

Ну и на всякий случай, пересечение контуров окружностей:

circles anySatisfy:
[:e |
(c center dist: e center)
between: (c radius - e radius) abs
and: c radius + e radius]


Alex Baran

(no subject)

23/11/06 10:57 (UTC)
Posted by [identity profile] aleksijb.livejournal.com
Недосмотрел. В этом случае:

Circle>>intersect: c2
^(self center dist: c2 center) < (self radius + c2 radius)

entities anySatisfy: [:entity | entity position circle intersect: c]

(no subject)

22/11/06 22:32 (UTC)
Posted by [identity profile] aleksijb.livejournal.com
Написал по этому случаю статью ;)

http://aleksijb.googlepages.com/circlesIntersections.htm