Здравствуйте, Глеб Алексеев, Вы писали:
ГА>Нет, я, к сожалению, учился не там, где это могли преподавать (эх, если бы я заканчивал MIT, я бы прошел SICP на первом курсе, а сейчас по ночам приходится ). На 90% все свои обрывочные знания о программировании добыл самостоятельно. Разве что мой будущий научный руководитель посоветовал обратить внимание на Пролог и подкинул пару тестовых задачек.
Понятно. А нам читали Пролог. Может быть поэтому у меня примеры программ в функциональном стиле вызывают неприятие
Как в школе: чтобы отвратить человека от русской литературы, нужно ему эту литературу насильно преподавать.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Понятно. А нам читали Пролог. Может быть поэтому у меня примеры программ в функциональном стиле вызывают неприятие E>Как в школе: чтобы отвратить человека от русской литературы, нужно ему эту литературу насильно преподавать.
. Ну, это не страшно. Т.к. Пролог — не функциональный. Я вот музыкальныю школу по классу ф-но закончил, по идее, музыку ненавидеть должен, а на деле это мне не мешает в меру сил жужжать на электрогитаре .
Здравствуйте, Глеб Алексеев, Вы писали:
ГА>Что я не так делаю ?
Все не так. Я ведь и в конец могу элементы добавить
V>>Голова списка (cons) — это может быть середина другого списка. ГА>Список — не объект, это значение, у него нет identity. Пока мы играем по функциональным правилам и 1) не интересуемся физическим равенством объектов (в ОКамл такое можно) и 2) не модифицируем mutable объекты внутри списков, нас совершенно не интересует, что несколько логически различных списков имеют физически общий хвост.
Именно, тем более не имеем право кешировать размеры списков, если нам не дано упрвлять их физическим представлением.
Здравствуйте, z00n, Вы писали:
Z>Здравствуйте, vdimas, Вы писали:
V>>Нет, не можно. Голова списка (cons) — это может быть середина другого списка.
Z>Про фичи тоже сильно написано, но тут не смог удержаться: cons — это конструктор, и не списка, а пары
cons — это термин ячейки, которые образуют связанный список, состав этой ячеки — элементы car и cdr. Имхо, ты перепутал термины с названиями одноименных ф-ий.
Одна ячейка cons — это и есть список. Ничто другое списком не является, кроме nil.
Здравствуйте, vdimas, Вы писали:
ГА>>Что я не так делаю ? V>Все не так. Я ведь и в конец могу элементы добавить
Ну, добавлять элементы в конец односвязного списка — неэффективная и оттого редкая операция. А двусвязные библиотечные списки есть (в extLib).
let cs_list_append (CSList (lst, n)) item = CSList (lst @ [item], n+1)
# let l1 = make_cs_list [1;2;3;4];;
val l1 : int cached_size_list = CSList ([1; 2; 3; 4], 4)
# let l2 = cs_list_append l1 5;;
val l2 : int cached_size_list = CSList ([1; 2; 3; 4; 5], 5)
На этот раз что не так?
Это бессмысленный пример, просто для демонстрации возможности. В классическом понимании указатель на узел — это уже список. В STL просто указатель на голову списка завернули в дополнительный объект, в котором реализовано все управление и дополнительно хранится размер. Никто не мешает то же самое сделать на ОКамле, что я и показал. Для пущей строгости инкапсулировать можно все в отдельный модуль, и работать с полученным cached_size_list'ом только через интерфейсные функции. Только не нужно это, ни длина не нужна, ни инкапсуляция списка, т.к. в работе со списками очень важен pattern matching. Но если в какой-то задаче окажется, что мне ежемиллисекундно нужна длина списка, уверяю вас, прокэширую и проигнорирую тот факт, что это невозможно .
V>>>Голова списка (cons) — это может быть середина другого списка. ГА>>Список — не объект, это значение, у него нет identity. Пока мы играем по функциональным правилам и 1) не интересуемся физическим равенством объектов (в ОКамл такое можно) и 2) не модифицируем mutable объекты внутри списков, нас совершенно не интересует, что несколько логически различных списков имеют физически общий хвост. V>Именно, тем более не имеем право кешировать размеры списков, если нам не дано упрвлять их физическим представлением.
Пока не сильно убедительно .
Если очень хочется управлять физическим представлением — пожалуйста, просто не нужно это.
Если нужно модифицировать, например, строку внутри списка — что, думаю, нужно нечасто, и обезопасить себя от изменений в других списках, достаточно строки явно скопировать. Пример:
# let ls1 = ["Hello"; "cruel"; "world"];;
val ls1 : string list = ["Hello"; "cruel"; "world"]
# let ls2 = List.tl ls1;;
val ls2 : string list = ["cruel"; "world"]
# let ls3 = "Yo"::ls2;;
val ls3 : string list = ["Yo"; "cruel"; "world"]
# let cruel = List.hd ls2;;
val cruel : string = "cruel"
# cruel.[0] <- 'C';;
- : unit = ()
# ls1;;
- : string list = ["Hello"; "Cruel"; "world"]
# ls2;;
- : string list = ["Cruel"; "world"]
# ls3;;
- : string list = ["Yo"; "Cruel"; "world"]
# let ls4 = List.map (String.copy) ls1;;
val ls4 : string list = ["Hello"; "Cruel"; "world"]
# cruel.[0] <- 'c';;
- : unit = ()
# ls1;;
- : string list = ["Hello"; "cruel"; "world"]
# ls4;;
- : string list = ["Hello"; "Cruel"; "world"]
#
з.ы. Во-первых, давайте жить дружно . А во-вторых, давайте сначала попробуем, а потом будем утверждать, что что-то невозможно.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>reductor,
>> ПК>Для любых, включающих необходимость своевременного освобождения внешних по отношению к программе ресурсов: файлы, сокеты, соединения с базами данных, хэндлы операционной системы и т.п. Основная проблема не столько в том, чтобы не забывать освобождать внешние ресурсы, а в том, чтобы надежно делать это в реальных условиях, когда, фактически, любая функция может выбросить исключение. >> >> http://www.rsdn.ru/Forum/Message.aspx?mid=1522461&only=1
Я понял вас прекрасно
Но вы в общем-то понимаете, что RAII — это хак сам по себе?
Причем, не очень красивый. Способ внести элемент хаоса и избежать проектирования.
Вот это "необязательно ограничено лексической областью видимости" ужасно на самом деле.
Сначала создаем связь и зависимость, а потом снимаем ограничения.
Но ладно, в конечном итоге каждый придумывает себе проблемы как хочет, конечно.
Если хотите получить такое в окамле — http://caml.inria.fr/pub/docs/manual-ocaml/libref/Gc.html
посмотрите на alarm, finalise, counters и тп. еще можно в общем-то описать то, что хочется через FFI и Си. + определить там свои функции-операторы для каких-то вещей или вообще свой интерфейс работы с классами через camlp4
Но имейтей в виду, еще, что в окамле классы используются сами по себе раз в 50 реже, чем в С++. И вообще нужность их там под вопросом, у окамла достаточно продвинутая система типов, чтобы классы что-то значили сами по себе.
Здравствуйте, reductor, Вы писали:
R>Но имейтей в виду, еще, что в окамле классы используются сами по себе раз в 50 реже, чем в С++. И вообще нужность их там под вопросом, у окамла достаточно продвинутая система типов, чтобы классы что-то значили сами по себе.
Интересно. Хочется задать вопрос: а к ООП ты вообще как относишься?
Личное: может давай(те) на "ты" перейдем? А то как-то неудобно получается...
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, reductor, Вы писали:
R>>Но имейтей в виду, еще, что в окамле классы используются сами по себе раз в 50 реже, чем в С++. И вообще нужность их там под вопросом, у окамла достаточно продвинутая система типов, чтобы классы что-то значили сами по себе.
E>Интересно. Хочется задать вопрос: а к ООП ты вообще как относишься?
Примерно так же, как и Dijkstra:
Object-oriented programming is an exceptionally bad idea which could only have originated in California.
Жаль только, я не такой умный как Дйкстра и мне потребовалось слишком много времени, чтобы прийти к тому же выводу (флеймить я здесь не буду).
E>Личное: может давай(те) на "ты" перейдем? А то как-то неудобно получается... :shuffle:
Здравствуйте, reductor, Вы писали:
E>>Интересно. Хочется задать вопрос: а к ООП ты вообще как относишься?
R>Примерно так же, как и Dijkstra: R>
Object-oriented programming is an exceptionally bad idea which could only have originated in California.
R>Жаль только, я не такой умный как Дйкстра и мне потребовалось слишком много времени, чтобы прийти к тому же выводу (флеймить я здесь не буду).
Круто. Не флейма ради, а для прояснения ситуации. Я так понимаю, что ООП тебя не миновала. Но затем пришло понимание, что функциональный (не знаю как точнее его назвать) дает больше, чем может дать ООП. Так?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
R>>>другое дело, что длину знать их — это очень редко, когда нужно. по индексу их никто не итерирует и т.п
V>>Если у меня свой собственный распределитель памяти, или я вызываю ф-ии АПИ ОС, или я пишу свой загрузчик и т.д.?
R>Это к чему?
Ну... к тому, что я системный программист, например. Разрабатываю автоматы, парсеры, сетевые ср-ва на соккетах и не только и пр
R>>>мощная штука, жаль, пока что без аппаратной поддержки медленнее, чем массивы
V>>Есть инциденты аппаратной поддержки кеширования списочных структур?
R>Ну, смотря что считать... R>http://citeseer.ist.psu.edu/187280.html
Ну... в том направлении, но вряд ли это применимо до подробностей ячейки cons из Лисп.
R>Вообще это большая тема с затрагиванием фон ноймана против гарварда и тп
Иногда и гарвард более приемлим. В embedded-устройствах, где на счету каждый бит, банально разная ширина шин дает неплохие бенефиты. Хотя, и там возможно написать тот же Forth, и вот уже наши данные — как программа В общем, одну модель сейчас беззастенчиво эмулируют на базисе другой (например .Net — противоположный пример) и обратно же.
R>http://www-rocq.inria.fr/syndex/
впечатляет, скачал — поиграюсь
R>А так, кое в каких очень немаленьких компаниях используется в очень немаленьких внутренних системах. R>Но те же молчат и другим велят
странная позиция
V>>Пролог — тоже весьма мощная штука, но блин чего-то не пошел в массы, даже удивительно... Даже с ОО- и императивными расширениями.
R>У пролога трагическая судьба R>Его загубили PC и слишком большие ожидания. Как и лисп R>Пролог-машины, лисп-машины, Красота. а тут персоналки, мать их. R>И все, всем считать байты
Не только. Многие программировали GUI, это было востребовано/модно и мало кто это умел. Сейчас вот ажиотаж уже лет 5 как прошел, должен снова проснуться интерес к другим подходам.
reductor wrote: > > Примерно так же, как и Dijkstra: > Object-oriented programming is an exceptionally bad idea which could > only have originated in California.
Прелесть какая! А откуда это, хотелось бы прочитать целиком...
Здравствуйте, eao197, Вы писали:
E>>>Интересно. Хочется задать вопрос: а к ООП ты вообще как относишься?
R>>Примерно так же, как и Dijkstra: R>>
Object-oriented programming is an exceptionally bad idea which could only have originated in California.
R>>Жаль только, я не такой умный как Дйкстра и мне потребовалось слишком много времени, чтобы прийти к тому же выводу (флеймить я здесь не буду).
E>Круто. Не флейма ради, а для прояснения ситуации. Я так понимаю, что ООП тебя не миновала. Но затем пришло понимание, что функциональный (не знаю как точнее его назвать) дает больше, чем может дать ООП. Так?
Функциональное программирование и ООП ортогональны друг другу, это не фундаментально разные или тем более противоположные вещи.
ООП может быть и в Хаскеле.
Просто, когда программируешь на том же хаскеле, понимаешь, что все ООП — это лишь неловкая попытка эмулировать некоторые эффекты тех же замыканий, higher order functions и нормальной модульности.
Хотя, в общем, даже без хаскеля можно это ощутить.
Но это долгий разговор.
Здравствуйте, Pzz, Вы писали:
Pzz>reductor wrote: >> >> Примерно так же, как и Dijkstra: >> Object-oriented programming is an exceptionally bad idea which could >> only have originated in California.
Pzz>Прелесть какая! А откуда это, хотелось бы прочитать целиком...
Ну это я так понимаю просто приписывается ему. Вполне вероятно, что он в свою очередь кого-то процитировал.
но гугл другого источника не находит.
а так: http://en.wikiquote.org/wiki/Edsger_Dijkstra
Здравствуйте, reductor, Вы писали:
R>Не уверен, что понял. R>Просто у меня это очень достаточно задача и на такие вещи я как правило не оглядываюсь.
(достаточно редкая?)
Да редкая, но задача, к тому же — специфичная. Например, я обрабатываю огромные потоки символьных данных на серверном приложении (EDI — направление). Для многопоточных систем аллокаторы работают так себе... Я пишу свой аллокатор, который работает только в контексте текущего потока/вызова и не требует синхронизации. Отработав один запрос, я просто опять "взвожу" аллокатор в начальное состояние. Т.е. я даже память у его блоков не освобождаю (не путать с вызовом методов-деструкторов), т.к. это незачем для локальных контекстов. Ты как раз про регионы говорил, в общем, похожий принцип, но "изобретенный" мною много лет назад.
Это какое-то нестандартное непереносимое расширение. Хотя, мотивация мне понятна.
R>>>Опять же — учитывая сколько в современном С++ своих объектов, а сколько всякого boost'a и прочего, сия возможность как-то не сильно обнадеживает.
V>>В любой программе содержится счетное кол-во типов. Гораздо более счетно кол-во типов действительно требующих своего аллокатора. Обычно в командах С++ давно есть несколько разновидностей аллокаторов, которые просто прикручиваются к требуемым типам.
R>не просто R>впрочем, это, опять же, зависит от специфики.
К счастью — просто (навскидку).
template<typename BaseT, typename AllocatorT>
class WrapWithAllocator : BaseT {
WrapWithAllocator() : BaseT() {}
template<typename T1> WrapWithAllocator(T1 t1) : BaseT(t1) {}
[...] // до скольки угодно аргументов, пользуемся тем,
// что до реального применения шаблоны не инстанциируются компиляторомvoid* operator new(size_t, void* buff) { return buff; }
void* operator new(size_t size) { return AllocatorT::alloc(size); }
void operator delete(size_t size) { AllocatorT::free(this, size); }
}
Я уже озвучивал, что С++ — достаточно универсальный язык, т.е. встраивать какие-либо механизмы (от низкоуровневых до высокоуровневых) в него наиболее легко. Другое дело, что сам язык унаследовал слишком много низкоуровневых и потенциально опасных конструкций от С, которые вовсе не нужны для успешного программинга на С++.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, reductor, Вы писали:
R>>Не уверен, что понял. R>>Просто у меня это очень достаточно задача и на такие вещи я как правило не оглядываюсь.
V>(достаточно редкая?)
Да. редкая. Что-то идет не так в матрице, я точно помню как писал это слово :)
V>Да редкая, но задача, к тому же — специфичная. Например, я обрабатываю огромные потоки символьных данных на серверном приложении (EDI — направление). Для многопоточных систем аллокаторы работают так себе... Я пишу свой аллокатор, который работает только в контексте текущего потока/вызова и не требует синхронизации. Отработав один запрос, я просто опять "взвожу" аллокатор в начальное состояние. Т.е. я даже память у его блоков не освобождаю (не путать с вызовом методов-деструкторов), т.к. это незачем для локальных контекстов. Ты как раз про регионы говорил, в общем, похожий принцип, но "изобретенный" мною много лет назад.
V>Я уже озвучивал, что С++ — достаточно универсальный язык, т.е. встраивать какие-либо механизмы (от низкоуровневых до высокоуровневых) в него наиболее легко. Другое дело, что сам язык унаследовал слишком много низкоуровневых и потенциально опасных конструкций от С, которые вовсе не нужны для успешного программинга на С++.
Да чем это все отличается от самого крайнего случая с тем же окамлом — написанием модуля на Си и линковкой с ним.
Это по-моему что-то психологическое, скорее :)
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, z00n, Вы писали:
Z>>Здравствуйте, vdimas, Вы писали:
V>>>Нет, не можно. Голова списка (cons) — это может быть середина другого списка.
Z>>Про фичи тоже сильно написано, но тут не смог удержаться: cons — это конструктор, и не списка, а пары
V>cons — это термин ячейки, которые образуют связанный список, состав этой ячеки — элементы car и cdr. Имхо, ты перепутал термины с названиями одноименных ф-ий.
V>Одна ячейка cons — это и есть список. Ничто другое списком не является, кроме nil.
Мне не жалко, я повторю: cons — это функция, название которой есть сокращение от constructor, которая является конструктором пары.
car и cdr — функции, а не "состав ячейки".
То, что вы называете "термин ячейки", называется "pair" или "dotted pair"
Или давайте зайдем с другого конца, (cons 42 43) — по вашему это связанный список? А так: (cons nil (cons 42 43))?
Не верите мне, наберите в гугле "cons definition", например.
Здравствуйте, Глеб Алексеев, Вы писали:
ГА>Ну, добавлять элементы в конец односвязного списка — неэффективная и оттого редкая операция.
какая разница, семантика должна быть полной
ГА>На этот раз что не так?
n+1 надо сделать у кеш-счетчика каждого элемента списка
да, это решаемо... но в таком виде — это уже не есть кешированное значение длины списка, т.е. смысл кеширования теряется (ты еще не забыл, с чего обсуждение началось? )
ГА>Это бессмысленный пример, просто для демонстрации возможности. В классическом понимании указатель на узел — это уже список. В STL просто указатель на голову списка завернули в дополнительный объект, в котором реализовано все управление и дополнительно хранится размер. Никто не мешает то же самое сделать на ОКамле, что я и показал. Для пущей строгости инкапсулировать можно все в отдельный модуль, и работать с полученным cached_size_list'ом только через интерфейсные функции. Только не нужно это, ни длина не нужна, ни инкапсуляция списка, т.к. в работе со списками очень важен pattern matching. Но если в какой-то задаче окажется, что мне ежемиллисекундно нужна длина списка, уверяю вас, прокэширую и проигнорирую тот факт, что это невозможно .
Да верю, только нафига это все? Если для каких-то задач потребуется ежемиллисекундно узнавать длину вектора, то лучше взять подходящий язык, не правда ли?
[скипнут код]
ГА>з.ы. Во-первых, давайте жить дружно . А во-вторых, давайте сначала попробуем, а потом будем утверждать, что что-то невозможно.
Понимаешь, универсальные языки потому и являются универсальными, что на них легко делать весьма многое,
вот за пол-часа на С# (мини-лисп, к нему легко прикрутить парсинг входного потока):
using System;
using System.Collections;
using System.Collections.Specialized;
namespace Probe {
public sealed class Lisp {
private static void Main() {
SETQ("A", LIST(LIST(1, 2), LIST(3, 4, LIST(5, 6), NIL)));
object a = SYMB("A");
PRIN1(a);
PRINC("\n");
PRIN1(CAR(a));
PRINC("\n");
PRIN1(CADR(a));
PRINC("\n");
PRIN1(CDAR(a));
PRINC("\n");
}
// краткая реализацияinternal class Cons {
internal object car, cdr;
public Cons(object car, object cdr) {
this.car = car;
this.cdr = cdr;
}
}
public static readonly object T = true;
public const object NIL = null;
private static readonly Hashtable s_symbols = CollectionsUtil.CreateCaseInsensitiveHashtable(100);
public static object ATOM(object o) {
if (o is Cons)
return NIL;
return T;
}
public static object CAR(object l) {
return ((Cons) l).car;
}
public static object CDR(object l) {
return ((Cons) l).cdr;
}
public static object CONS(object car, object cdr) {
return new Cons(car, cdr);
}
public static object LIST(params object[] objects) {
object list = NIL;
for (int i = objects.Length - 1; i >= 0; i--)
list = CONS(objects[i], list);
return list;
}
public static object SETQ(string symbol, object value) {
s_symbols[symbol] = value;
return value;
}
public static void _PRINC(string str) {
Console.Write(str);
}
public static object PRINC(object o) {
if (o == NIL)
_PRINC("NIL ");
else if (ATOM(o) == T)
_PRINC(o.ToString());
else
throw new ArgumentException("param #1 is not an atom");
return o;
}
public static object PRIN1(object o) {
if (ATOM(o) == T)
PRINC(o);
else {
PRINC("(");
print_list((Cons) o);
PRINC(")");
}
return o;
}
internal static void print_list(Cons list) {
PRIN1(list.car);
if (list.cdr != NIL) {
PRINC(" ");
if (ATOM(list.cdr) == T)
PRINC(list.cdr);
else
print_list((Cons) list.cdr);
}
}
public static object SYMB(string symbol) {
return s_symbols[symbol];
}
public static object CADR(object l) {
return CAR(CDR(l));
}
public static object CDAR(object l) {
return CDR(CAR(l));
}
// и т.д. и т.п.
}
}
Здравствуйте, vdimas, Вы писали:
V>n+1 надо сделать у кеш-счетчика каждого элемента списка
Нет, не надо. Точно так же, как не надо это в С++.
А спорить дальше не вижу смысла, уже все сказал и не знаю, как по-другому:
ГА>>В классическом понимании указатель на узел — это уже список. В STL просто указатель на голову списка завернули в дополнительный объект, в котором реализовано все управление и дополнительно хранится размер. Никто не мешает то же самое сделать на ОКамле, что я и показал.
V>Понимаешь, универсальные языки потому и являются универсальными, что на них легко делать весьма многое, V>вот за пол-часа на С# (мини-лисп, к нему легко прикрутить парсинг входного потока):
Уверен, многие присутствующие за полчаса и с гораздо большим успехом изобразят мини-лисп на ОКамле. Да еще и встроят его поддержку в исходном коде с помощью camlp4.
Вам нравится утверждать, что С++, Ява и C# — достаточно универсальные языки, и все остальные можно посылать подальше, даже не попробовав их на тех задачах, в которых они сильны (кстати, вы писали, что разрабатываете парсеры — так это же задача номер раз для ML-языков, примеров найдете немало). Я убежден в обратном. Кое-кто поопытнее меня убежден в этом настолько, что делает на этом бизнес: Работа на OCaml в Москве
V>Да верю, только нафига это все? Если для каких-то задач потребуется ежемиллисекундно узнавать длину вектора, то лучше взять подходящий язык, не правда ли?
и
V>Понимаешь, универсальные языки потому и являются универсальными, что на них легко делать весьма многое, V>вот за пол-часа на С# (мини-лисп, к нему легко прикрутить парсинг входного потока):
Я или чего-то не понимаю или здесь утверждение, что окамл или лисп менее универсальны, чем С#?