LINUX.ORG.RU

извраты на плюсах =)


0

0

Навеяно http://www.linux.org.ru//view-message.jsp?msgid=822951 и переизбытком свободного времени вследствие присутствия на двухчасовой лекции по яве =)

Всем любителям C++ посвящается: реализация Conway's Game of Life целиком на template metaprogramming, без использования boost::mpl и других сторонних костылей. Исключительно ISO C++ =)

Исправления и добавления крайне приветствуются...

★★★★

Ответ на: комментарий от int19h

> Так ты все-таки скажи - там рекурсия конечная или бесконечная? =) А то у тебя
> получается, что когда на _уже скомпилированный_ код вызывается !! (получить N-й > элемент), то она конечная - т.к. завершается.

Ну, правильно, мы ведь его тогда заставляем число фиббоначи только для N элементов посчитать. Соответственно рекурсия N-ой длины.

> А когда вызывается функция length (длина списка), то рекурсия бесконечная - прога виснет.

А тут как раз странно, что не могли как-нить придумать обозначение бесконечности, тут же понять, что длина бесконечна и вывести это число (infinity). Ну и не виснуть.

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

> Ну, правильно, мы ведь его тогда заставляем число фиббоначи только для N элементов посчитать. Соответственно рекурсия N-ой длины.

Еще раз: посмотри внимательно на код - там _бесконечная_ рекурсия. Код компилится в _объектный файл_, после чего исходник стирается - т.е. изменять его "на лету" по мере необхдимости уже не получится.

И почитай все-таки в той же википедии про рекурсию и про хаскель =)

int19h ★★★★
() автор топика
Ответ на: комментарий от int19h

> Еще раз: посмотри внимательно на код - там _бесконечная_ рекурсия.
Какая разница, как эта бесконечность выглядит?
Компилятор разумеется не дурак и не будет пытаться вычислять все элементы этого
вашего бесконечного списка.
Вот вам точно такой же "бесконечный массив" на C++ (с примером использования
сразу):
---
#include <stdlib.h>
#include <stdio.h>
class Tfibc{
    public:
        int operator[](int n){ calcfibc(n); };
    private:
        int calcfibc (int n, int a=1, int b=0)
        {
            if (n<=1) return a;
            return calcfibc (n-1, a+b, a);
        };
};

int main(int argc, char *argv[])
{
    Tfibc fibc;
    fprintf (stdout,"%d\n",fibc[atoi(argv[1])]);
}
---
length конечно нету, ну да это мелочи :-)
Кстати, если я не ошибаюсь для haskell есть возможность "скомпилировать" его в
c-шный код, ну и посмотреть как там это реализовывается..

> Код компилится в _объектный файл_, после чего исходник стирается - т.е. 
> изменять его "на лету" по мере необхдимости уже не получится. 
Точно так же будет работать вышеприведённый класс :-)

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

Он не такой же. Здесь ты каждый элемент будешь КАЖДЫЙ РАЗ вычислять. В Haskell - только один раз.

anonymous
()
Ответ на: комментарий от unDEFER

Ты - очень глупый человек. Дабы понять, что структура бесконечная, её всю необходимо вычислить.

anonymous
()
Ответ на: комментарий от unDEFER

> int operator[](int n){ calcfibc(n); };
О, кстати! Я и не думал что так можно писать :-)
По идее тут ошибка (компилятор только warning при -Wall выдаёт) - return, то я забыл!
Но компилятор догадывается что я имел ввиду ;-)

unDEFER ★★★★★
()
Ответ на: комментарий от anonymous

> Дабы понять, что структура бесконечная, её всю необходимо вычислить.
Да? А как же lazy evaluation???
Пример, то не сложный (даже тривиальный) - вполне можно было бы учесть!

unDEFER ★★★★★
()
Ответ на: комментарий от anonymous

> Он не такой же. Здесь ты каждый элемент будешь КАЖДЫЙ РАЗ вычислять. В Haskell - только один раз.
Это не суть важно (и вообще зависит от компилятора - не факт, что не найдётся компилятора, который будет иметь именно такую реализацию)!
Иногда (когда важно съэкономить память, только не во-втором случае) будет лучше подходить именно такая реализация.
Иногда (когда известно что числа буду запрашивать по возрастающей) будет удобно запоминать только результат каждого вызова (т.е. если был вызов fibc[n], то запомнить только это число fibc[n]). Это конечно же идеальный вариант и для памяти, и для процессорного времени (но всё же он не подходит для следующего случая).
А иногда (когда известно, что числа будут запрашивать по убывающей) будет уже удобно запоминать именно все числа посчитанные при каждом вызове (т.е. если был вызов fibc[n], то запомнить все числа fibc[1], fibc[2], fibc[3].... fibc[n]), но тогда это может быть плохо для памяти.

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

> Точно так же будет работать вышеприведённый класс :-)

Не так же. Ты просто запихал в класс функцию. А у меня - заметил? - в классах, кроме print, только одни константы. Посмотри повнимательней.

int19h ★★★★
() автор топика
Ответ на: комментарий от unDEFER

> О, кстати! Я и не думал что так можно писать :-) Но компилятор догадывается что я имел ввиду ;-)

Он не догадался. Просто на x86-архитектурах результат вычисления выражения практически всегда помещается в регистр EAX, и он же используется для возврата значения функции.

int19h ★★★★
() автор топика
Ответ на: комментарий от unDEFER

> Это не суть важно (и вообще зависит от компилятора - не факт, что не найдётся компилятора, который будет иметь именно такую реализацию)!

Ты так и не понял.

Предположим, в хаскелле я напишу такой вот код:

xs = [x | x <- fibs, x < 20]

что даст мне все числа Фибоначчи меньше двадцати. Однако же при этом программа не будет рассчитывать каждое число рекурсивно - она возьмет первое, проверит условие, потом второе, еще проверка, потом третье выведет из первого и второго etc.

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

И компиляторы тут - не при чем. Это семантика.

int19h ★★★★
() автор топика
Ответ на: комментарий от unDEFER

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

> Да? А как же lazy evaluation???

Еще раз и по слогам: чтобы определить, что длина рекурсивной последовательности равна бесконечности, в общем случае, ее надо вычислить _всю_.

int19h ★★★★
() автор топика
Ответ на: комментарий от int19h

> Не так же. Ты просто запихал в класс функцию.
Блин, а в Haskell точно так же запихал в функцию всё это дело _компилятор_.
Смысл то тот же. Главное, что снаружи это выглядит абсолютно как массив. Так же и в Haskell - снаружи выглядит как список, а внутри это по-любому функция (хотя она и сложнее чем у меня).

> Однако же при этом программа не будет рассчитывать каждое число рекурсивно - она возьмет первое, проверит условие, потом второе, еще проверка, потом третье выведет из первого и второго etc.
Вам что дополнить этот класс буфером? Это не долго. Вы этого хотите?

> в общем случае, ее надо вычислить _всю_.
В общем случае чтобы вынимать из списка элементы нужно сначала этот список _полностью_ записать. Но Haskell этого же не делает.

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

Эта тема началась несколько дней назад. Не был бы ты дураком, успел бы уже изучить Хаскелль и перестать тормозить.

anonymous
()
Ответ на: комментарий от anonymous

Конечно, конечно..
Когда два человека друг друга не понимают, им будет казаться, что другой тормозит... Причём один из них скорее всего будет всё же прав :-)
Мне почему-то кажется, мы говорим о разных вещах :-)

unDEFER ★★★★★
()
Ответ на: комментарий от int19h

> А у меня - заметил? - в классах, кроме print, только одни константы.
> Посмотри повнимательней.
Кстати эту часть фразы я не правильно воспринял :-= Я думал мы ешё
о Haskell'е говорим :-)
Да уж, у меня тоже получается извращаться:
---
#include <stdlib.h>
#include <stdio.h>
template <int n>
    struct fibc
{
    const static int z=fibc<n-2>::z+fibc<n-1>::z;
};

template <> struct fibc<1> { const static int z=1; };
template <> struct fibc<2> { const static int z=1; };

int main(int argc, char *argv[])
{
    fprintf (stdout,"%d\n",fibc<8>::z);
}
---
Но это уже как раз совсем не так как в Haskell
(template нельзя скомпилировать в бинарник)

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

> Блин, а в Haskell точно так же запихал в функцию всё это дело _компилятор_.

Ты вообще понимаешь, что такое "семантика"?

Рекурсивным является _определение_. Компилятор может творить там все что угодно - например, развернуть его в цикл в порядке оптимизации. Меня это не волнует - я не работаю на том уровне абстракции. Как там реализовывается моя рекурсия - анализом кода и разворачиванием его inline, или определением stub'ов с последующим их вызовом - это не мое дело ...

Так вот, я определяю бесконечный список, в более общем случае - множество. А ты - всего лишь функцию отображения множества натуральных чисел на множество чисел Фибоначчи. Ты понимаешь разницу между множеством и определяющей его функцией? Хотя бы на уровне "на список я могу сделать map/zip/etc"?

> Вам что дополнить этот класс буфером? Это не долго. Вы этого хотите?

Нет, я не этого хочу. Но если ты хочешь написать на плюсах хаскелль (или же в общем случае доказать, что это возможно - пусть даже и с #@%$ким синтаксисом) - то флаг тебе в руки.

int19h ★★★★
() автор топика
Ответ на: комментарий от unDEFER

> Но это уже как раз совсем не так как в Haskell (template нельзя скомпилировать в бинарник)

Вот это как раз таки куда ближе к хаскеллю. Другой вопрос, что оно интерпретируется во время компиляции, но это совершенно десятое дело. По _семантике_ код на шаблонах ближе к хаскеллю хотя бы потому, что fib<n> тоже вычисляется лениво, причем только один раз для каждого n. Если ты там еще задашь head и tail, то вообще чистый хаскелль получится =)

int19h ★★★★
() автор топика
Ответ на: комментарий от int19h

> Рекурсивным является _определение_.
Согласен!

> это не мое дело ...
Ну и прекрасно :-)

> Ты понимаешь разницу между множеством и определяющей его функцией?
Понимаю, но также я понимаю, что реально никакого множетва _целиком_ Haskell не хранит. Хотя вас это и не интересует :-)

> Нет, я не этого хочу.
Я тоже

> то флаг тебе в руки.
Не я лучше продолжу писать распонаватель рукописного текста - полезней будет.
А кстати, компилятор Haskell и без того не на сях/плюсах написан?

> Другой вопрос, что оно интерпретируется во время компиляции, но это совершенно десятое дело.
А мне как раз это кажется важным (из-за чего и весь сыр-бор выше).

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

Ну, всё. Кажется наконец-то этот топик заканчивается :-)

unDEFER ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.