LINUX.ORG.RU

scheme-mode и define

 , ,


1

2

Хочу изучить диалект лиспа scheme, взял книжку «Структура и интерпретация компьютерных программ». Включаю scheme-mode в емаксе, выполняю простейший пример:

 (define size 2) 
выполняю (^x^e), выводит ошибку - void function define. Что я делаю не так?


я совсем в emacs не разбираюсь, но предполагаю, что ты его отправил интерпретатору emacs lisp вместо собственно scheme. scheme-mode же, наверное, только подсветку синтаксиса и прочие мелочи регулирует, нет? если не прав, помидорами не кидать.

sevenredlines
()

Что я делаю не так?

Пытаешься выполнить выражение Emacs Lisp'а. Изучи scheme-mode, какие у нее комбо. C-h m тебе в помощь.

iVS ★★★★★
()

поставь drracket и не парь себе мозг

x4DA ★★★★★
()
Ответ на: комментарий от seg-fault

Если программирование прям «твое» или есть опыт работы программистом, смотри на Common Lisp

Не соглашусь. Я перешёл с Common Lisp на Scheme (точнее на Racket). На Common Lisp удобно делать прототипы. Удобно делать среду для программиста (с возможностью подкрутить что угодно в любой момент). Но именно эти подходы учат плохому как только требуется написать программу не для программиста, а для пользователя.

Scheme вообще и Racket в частности, предоставляя те же возможности, продвигают более математический подход. Кроме того, некоторые алгоритмы из Scheme на Common Lisp реализовать нельзя.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

Не соглашусь

Переживу

Кроме того, некоторые алгоритмы из Scheme на Common Lisp реализовать нельзя

Флаг вам в руки. Мне не интересно, что там напридумывали в scheme/racket/clojure/picolisp/newlisp/whatever

seg-fault
()
Ответ на: комментарий от seg-fault

Флаг вам в руки. Мне не интересно, что там напридумывали в scheme/racket/clojure/picolisp/newlisp/whatever

Зачем же ты тогда пытаешься советовать в области, в которой не разбираешься?

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

Какие?

Например, написать функцию, которая получает функцию упорядоченного обхода коллекции (map-list, map-tree, ...) и две большие коллекции, затем сравнивает поэлементно содержимое этих коллекций, при первом несовпадении элементов, возвращает ложь. Если несовпадающих нет, то истину. Копировать коллекции целиком в списки запрещено (коллекции большие, возможно не влезающие в оперативную память, например, выборка по БД).

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

Наверное, речь о ТСО и продолжениях, которых в общелиспе нет

TCO местами (в отдельных реализациях есть).

А так, да. Продолжения, метки продолжений, эфемероны, ячейки нити(thread cell), синхронизируемые события, ...

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

У меня geiser-mode не получилось толком настроить: конфликтовал с существующими пакетами (с evil-mode в частности).
В итоге остановился на racket-mode.

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

Например, написать функцию, которая получает функцию упорядоченного обхода коллекции (map-list, map-tree, ...) и две большие коллекции, затем сравнивает поэлементно содержимое этих коллекций, при первом несовпадении элементов, возвращает ложь.

Э-м… Итераторы не вчера придумали, например. =)

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

Итераторы не вчера придумали, например

Да. Только в Common Lisp нет возможности из map* сделать итератор. Хотя yield даже в питоне есть

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

Э-м… Итераторы не вчера придумали, например. =)

В Common Lisp даже там где итераторы есть (with-map-hash-iterator), этот итератор нельзя передать в функцию. Потому что итератор не функция, а macrolet.

В отличие от Scheme, где всё, что может быть функцией реализовано как функция (можно сравнить unwind-protect — макрос, dynamic-wind — функция, with-open-file — макрос, call-with-input-file — функция)

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

Да. Только в Common Lisp нет возможности из map* сделать итератор. Хотя yield даже в питоне есть

Зачем делать итератор из map*?

В Common Lisp даже там где итераторы есть (with-map-hash-iterator), этот итератор нельзя передать в функцию. Потому что итератор не функция, а macrolet.

Это проблемы стандартной реализации хеш-таблиц, при чём тут язык? Это никак не свидетельствует о невозможности реализовать этот алгоритм.

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

Зачем делать итератор из map*?

Затем, что в API к чему-нибудь может быть только map.

Пусть, для конкретики, тебе надо написать алгоритм сравнения, который может обрабатывать хэши, символы пакета, списки, вектора и произвольные коллекции, описанные пользователем алгоритма. Что будешь передавать в качестве итератора в алгоритм? От пользователя, конечно, можно потребовать и итератор, но для хэша-то он не существует, для символов пакета — тоже. Будешь писать три версии алгоритма почти копипастом?

Это проблемы стандартной реализации хеш-таблиц, при чём тут язык? Это никак не свидетельствует о невозможности реализовать этот алгоритм.

Именно алгоритм «имея только map, сравнить две коллекции с выходом из по первому несовпадающему элементу, не копируя коллекции» на Common Lisp реализовать нельзя. И хеш-таблицы тут всего лишь иллюстрация.

Если недостаточно, могу ещё примеры: написать на Common Lisp хэш со слабыми ссылками (по ключу и по значению), добавить в информацию по фреймам в отладчике информацию о состоянии динамической переменной, сделать реализацию зелёных тредов.

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

Затем, что в API к чему-нибудь может быть только map.

А может и не быть.

Именно алгоритм «имея только map, сравнить две коллекции с выходом из по первому несовпадающему элементу, не копируя коллекции» на Common Lisp реализовать нельзя. И хеш-таблицы тут всего лишь иллюстрация.

Ну знаешь. Почему не «имея только lambda» или «имея только +»? Встречный вопрос: как в Racket реализован type-of?

написать на Common Lisp хэш со слабыми ссылками (по ключу и по значению)

Не?

сделать реализацию зелёных тредов.

Ну, люди что-то такое делают. Не знаю насчёт качества, но это никак не тянет на «невозможно».

Встречный вопрос: в Racket это всё реализовано средствами базовой Scheme с помощью продолжений или это вшито в базовый Racket?

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 1)
Ответ на: комментарий от monk

Хотя yield даже в питоне есть

Да чо уж там, yield даже в пыхе есть.

Только в Common Lisp нет возможности из map* сделать итератор

Но можно сделать итератор не из map, и, при наличии макросов, большой разницы для пользователя нет.

no-such-file ★★★★★
()
Ответ на: комментарий от korvin_

Встречный вопрос: как в Racket реализован type-of?

Если классически, то никак, потому что идея выделенного типа для значения вредна. То есть, число 1 имеет тип 1, byte, fixnum, positive-fixnum, integer, ...? Правильный путь — использовать предикаты для проверки принадлежности нужному типу.

Если очень надо в стиле CL, то cond на несколько десятков строк.

Не?

Не. Там не «написать», а «использовать реализацию встроенную в компилятор».

Ну, люди что-то такое делают

«with help of CL-CONT for continuations»

Жуткие тормоза и неработоспособность при использовании кода, не обработанного CL-CONT. CL-CONT — макрос переделывающий код в CPS.

Встречный вопрос: в Racket это всё реализовано средствами базовой Scheme с помощью продолжений или это вшито в базовый Racket?

Средствами продолжений. Но они слегка расширены по сравнению с базовой Scheme. Есть continuation mark — позволяют прицеплять к фрейму любые данные => писать любой отладчик. Есть barriers — откуда начинается продолжение (для классической Scheme это строка ввода интерпретатора). Есть три вида продолжений: как в Scheme (undelimited), delimited и escape. Последние два можно реализовать через обычные, но отдельные реализации на уровне VM эффективней.

monk ★★★★★
()
Ответ на: комментарий от no-such-file

Но можно сделать итератор не из map, и, при наличии макросов, большой разницы для пользователя нет.

Ну сделай итератор для cl:hash-table. Который можно передать в функцию. Или предложи другой способ для обобщенного алгоритма для коллекций (должен обрабатывать векторы, списки, хэши, произвольные пользовательские коллекции).

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

Встречный вопрос: в Racket это всё реализовано средствами базовой Scheme с помощью продолжений или это вшито в базовый Racket?

Немножко не понял: что именно «это всё»? Если зелёные нити, то на продолжениях. Но в ядре Racket есть нити (на базе нитей ОС), есть эфемероны (базовый блок для любых коллекций слабых ссылок), есть метки продолжений и т. д.

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

Если классически, то никак, потому что идея выделенного типа для значения вредна. То есть, число 1 имеет тип 1, byte, fixnum, positive-fixnum, integer, ...? Правильный путь — использовать предикаты для проверки принадлежности нужному типу.

Если очень надо в стиле CL, то cond на несколько десятков строк.

Очень удобно при попытке реализовать «внешнюю» (по отношению к типам) диспетчеризацию, ЕМНИП, смотрел я как-то исходники Swindle или чего-то подобного, не вижу в «cond на несколько десятков строк» ничего правильного. А уж для многообразия типов Racket понадобится не одна сотня строк. + Нужно предусмотреть предоставление возможности добавления в этот cond пользовательских структур, экзистенциальных типов и прочих.

Предикаты и в CL никто писать не запрещает через deftype, например, только на практике от них проку не так много, ИМХО, в отличие от удобной рефлексии.

Немножко не понял: что именно «это всё»?

То, что ты перечислил.

Но в ядре Racket есть нити (на базе нитей ОС), есть эфемероны (базовый блок для любых коллекций слабых ссылок), есть метки продолжений и т. д.

Ну вот, куча примитивов таки зашита в ядро, а не реализованы библиотеками (для) Scheme.

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 2)
Ответ на: комментарий от korvin_

А уж для многообразия типов Racket понадобится не одна сотня строк. + Нужно предусмотреть предоставление возможности добавления в этот cond пользовательских структур, экзистенциальных типов и прочих.

Пользовательские типы бывают только через struct (и в случае swindle через defclass). Поэтому полный class-of

 (set! class-of
      (lambda (x)
        (cond [(instance?    x) (instance-class x)]
              [(struct? x)
               (let-values ([(type _) (struct-info x)])
                 (if type (struct-type->class type) <opaque-struct>))]
              [(procedure?   x) (cond [(parameter? x) <parameter>]
                                      [(primitive? x) <primitive-procedure>]
                                      [else <procedure>])]
              [(string?      x) (if (immutable? x) <immutable-string> <string>)]
              [(pair?        x) (if (list? x) <nonempty-list> <immutable-pair>)]
              [(null?        x) <null>]
              [(symbol?      x) (if (keyword? x) <keyword> <symbol>)]
              [(number?      x)
               (if (exact? x)
                 (cond [(integer?  x) <exact-integer>]
                       [(rational? x) <exact-rational>]
                       [(real?     x) <exact-real>]
                       [(complex?  x) <exact-complex>]
                       [else <exact>]) ; should not happen
                 (cond [(integer?  x) <inexact-integer>]
                       [(rational? x) <inexact-rational>]
                       [(real?     x) <inexact-real>]
                       [(complex?  x) <inexact-complex>]
                       [else <inexact>]))] ; should not happen
              [(boolean?     x) <boolean>]
              [(char?        x) <char>]
              [(bytes?       x) (if (immutable? x) <immutable-bytes> <bytes>)]
              [(path?        x) <path>]
              [(vector?      x) <vector>]
              [(mpair?       x) <mutable-pair>]
              [(eof-object?  x) <end-of-file>]
              [(input-port?  x)
               (if (file-stream-port? x) <input-stream-port> <input-port>)]
              [(output-port? x)
               (if (file-stream-port? x) <output-stream-port> <output-port>)]
              [(void?           x) <void>]
              [(box?            x) <box>]
              [(weak-box?       x) <weak-box>]
              [(regexp?         x) <regexp>]
              [(byte-regexp?    x) <byte-regexp>]
              [(promise?        x) <promise>]
              [(keyword?        x) <keyword>]
              [(semaphore?      x) <semaphore>]
              [(hash-table?     x) <hash-table>]
              [(thread?         x) <thread>]
              [(subprocess?     x) <subprocess>]
              [(syntax?         x)
               (if (identifier? x) <identifier-syntax> <syntax>)]
              [(namespace?      x) <namespace>]
              [(custodian?      x) <custodian>]
              [(tcp-listener?   x) <tcp-listener>]
              [(security-guard? x) <security-guard>]
              [(will-executor?  x) <will-executor>]
              [(struct-type?    x) <struct-type>]
              [(inspector?      x) <inspector>]
              [(pseudo-random-generator? x) <pseudo-random-generator>]
              [(compiled-expression? x) <compiled-expression>]
              [else <unknown-primitive>])))

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

(define (type-of x)
  (vector-ref (struct->vector x) 0))

> (struct a (x))
> (type-of (a 1))
'struct:a
> (type-of 2)
'struct:fixnum-integer
> (type-of #(1 2))
'struct:vector

Ну вот, куча примитивов таки зашита в ядро, а не реализованы библиотеками (для) Scheme.

Разумеется. Поток ОС или сокет можно реализовать либо через ядро языка, либо через FFI к сишной библиотеке. В языке со сборщиком мусора намного лучше через ядро языка. К Common Lisp меня как раз претензия в том, что необходимых структур нет в ядре языка и их невозможно адекватно прикрутить через FFI.

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

Пользовательские типы бывают только через struct (и в случае swindle через defclass)

И в случае <ещё какого-нибудь расширения> через <ещё что-нибудь>.

Кажется ты забыл каналы, события, объекты, интерфейсы, immutable-hash-table, contract? etc., просто port?

Или это всё определяется через struct-type->class? И что там с opaque-struct?

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

Про подтипирование (та же numeric tower, порты, каналы, хэш-таблицы) я уж и не спрашиваю.

По-твоему эта cond-портянка лучше, чем пара коротких отдельных методов итерации хэш-таблиц? =)

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

Кажется ты забыл каналы, события, объекты, интерфейсы, immutable-hash-table, contract? etc., просто port?

«просто port» не бывает. Он или input или output, immutable-hash-table — подтип hash-table. Хотя для него, конечно, можно описать отдельный «тип». contract? — всего лишь функция. Каналы и события — да.

Про подтипирование

В CL подтипирование в type-of тоже весьма условное.

* (type-of 2)

(INTEGER 0 4611686018427387903)

Экзистенциальные типы тоже полноценные типы

И на какое значение они должны возвращаться?

Ну и (vector-ref (struct->vector x) 0) работаёт на всём :-)

И в случае <ещё какого-нибудь расширения> через <ещё что-нибудь>

Если я напишу свою объектную систему в CL, то у меня вообще нет шансов заставить type-of показывать из неё типы.

monk ★★★★★
()

Всем спасибо за развёрнутый ответ. =) Поставил guile, всё работает.

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

«просто port» не бывает.

Структуры данных нет, а тип есть. Если тебе важны лишь структуры данных (а абстрактные типы — нет), то ответ на твой вопрос про тип 1 очевиден и однозначен. =)

immutable-hash-table — подтип hash-table

Вообще, не совсем, т.к. методы работы с hash-table не подходят для immutable-hash-table, т.е. принцип подстановки не работает, например.

contract? — всего лишь функция

А chaperone-contract? impersonator-contract? flat-contract? list-contract?

И на какое значение они должны возвращаться?

В смысле? Не понял этой фразы.

Если я напишу свою объектную систему в CL, то у меня вообще нет шансов заставить type-of показывать из неё типы.

На основе чего ты её будешь писать? Замыканий? Чем тут Racket поможет? Разве что экзистенциальными типами, но у тебя пока какие-то вопросы по поводу них. Структур? Для определения нового типа вполне можно использовать defstruct, один чёрт структуры для хранения полей нужны. Но зачем это всё, если есть MOP и можно написать любой нужный метакласс под «свою объектную систему», насколько я знаю.

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

Ну сделай итератор для cl:hash-table

Берём итератор по списку, типа такого:

(defun make-iter (list)
  (lambda ()
    (prog1
	(first list)
      (setf list (cdr list)))))

(defun test (iter)
  (print (funcall iter))
  (print (funcall iter))
  (print (funcall iter))
  (print (funcall iter)))

CL-USER> (test (make-iter '(1 2 3)))

1 
2 
3 
NIL 
Используем для итерации по списку ключей хэша. Как-то так.

no-such-file ★★★★★
()
Ответ на: комментарий от korvin_

Вообще, не совсем, т.к. методы работы с hash-table не подходят для immutable-hash-table, т.е. принцип подстановки не работает, например.

Некоторые методы. И у них контракт вида (and/c hash? (not/c immutable?))

А chaperone-contract? impersonator-contract? flat-contract? list-contract?

> (procedure? (flat-contract 1))
#t

Структуры данных нет, а тип есть

Мы всё ещё говорим о type-of? Для какого объекта должен возвращаться тип port? Для какого значения должен возвращаться экзистенциальный тип?

На основе чего ты её будешь писать? Замыканий? Чем тут Racket поможет? Разве что экзистенциальными типами, но у тебя пока какие-то вопросы по поводу них.

Swindle написан на замыканиях. И class-of в нём знает про введённые им типы.

Но зачем это всё, если есть MOP и можно написать любой нужный метакласс под «свою объектную систему», насколько я знаю.

Примитивный пример. Надо сделать API в стиле (С++):

class A {
public:
    int print();
    int print(string x);
};

class B {
public:
    string print;
};

void print() {
}

В CLOS начинаются проблемы с именами методов и аксессоров. Появляется необходимость сваять на коленке Smalltalk-подобный OO с синтаксисом вида

(defvar a (make 'A))
(call a print)
(call a print "123")

И, внезапно, (type-of a) возвращает не 'A, а 'function. И сделать ничего нельзя :-(

monk ★★★★★
()
Ответ на: комментарий от no-such-file

Используем для итерации по списку ключей хэша

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

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

А chaperone-contract? impersonator-contract? flat-contract? list-contract?

> (procedure? (flat-contract 1))
#t

Я думал, вполне очевидно, что, если мы задаём эти вопросы, то нам важно различие между procedure и *-contract.

Мы всё ещё говорим о type-of? Для какого объекта должен возвращаться тип port? Для какого значения должен возвращаться экзистенциальный тип?

Во, я просто формулировку не понял. Очевидно, что type-of будет возвращать конкретный тип, но с помощью subtypep или какого-нибудь предиката а ля (satisfies (type-of x) 'port) мы можем узнать нужную информацию. Суть в возможности определения общих для всех подтипов port? методов (типа close) и вызова метода надтипа при отсутствии метода данного типа, например.

То же самое с экзистенциальными типами, они лишь вставляют свою метку в указатель вместо метки базового типа. В Go это работает как (type foo int), например. Фактически структура остаётся та же, но тип, приписанный значению — другой. Ты «поиграй» с экзистенциальными типами в Racket. Система в рантайме знает, что к значению приписан екзистенциальный тип (если значение было создано функцией, которая по своему контракту возвращает значение этого экзистенциального типа). Например:

Specifically, the null? predicate (and many other predicates) return #f for #:∃ contracts, and changing one of those contracts to any/c means that null? might now return #t instead, resulting in arbitrarily different behavior depending on how this boolean might flow around in the program.

Причём в этой цитате не только про то, что #:∃ с одной стороны самостоятельные типы, но и про то, что их можно легко и, главное, случайно «разыменовать» к фактическому типу, тем самым просто разрушив инкапсуляцию и полностью нивелировать пользу от наличия этой фичи.

В CLOS начинаются проблемы с именами методов и аксессоров. Появляется необходимость сваять на коленке Smalltalk-подобный OO с синтаксисом вида

Это проблемы не CLOS, а пакетной системы и вообще способа CL работать с символами.

И, внезапно, (type-of a) возвращает не 'A, а 'function. И сделать ничего нельзя :-(

Да конечно, не пори чушь, такие методы можно размещать лямбдами в class-storage, например. А насчёт call, ты так говоришь, как будто send в Racket'овом ООП отменили. Который, кстати, макрос, а не функция, в отличие от.

Swindle написан на замыканиях. И class-of в нём знает про введённые им типы.

Расскажи, как он отличает произвольное замыкание от экземпляра какого-нибудь своего класса?

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

Так нету итератора по ключам хэша

Не по ключам, а по списку ключей

функции для получения списка ключей тоже нет

Нет, т.к. не нужно, есть loop.

CL-USER> (defparameter *hash* (make-hash-table))
*HASH*
CL-USER> (setf (gethash :a *hash*) 1)
1
CL-USER> (setf (gethash :b *hash*) 2)
2
CL-USER> (setf (gethash :c *hash*) 3)
3
CL-USER> (test (make-iter (loop for key being the hash-keys of *hash* collect key)))

:A 
:B 
:C 
NIL 

предлагаешь любую коллекцию для обхода преобразовывать в список

А в чём проблема запилить подобный итератор для любой коллекции?

преобразовывать в список, а только потом обходить?

Кстати говоря, а что там вообще «обходить» то? Векторы и строки что ли, нафига это нужно? Я понимаю, когда есть два дерева которые нужно обойти параллельно, тут итератор/генератор удобен, так ведь деревья в лиспе строятся на списках, как и ручной стек, который потребуется для итератора по дереву.

no-such-file ★★★★★
()
Ответ на: комментарий от monk

Так нету итератора по ключам хэша. Кстати, функции для получения списка ключей тоже нет.

(defstruct keys-iterator (keys :reader keys-iterator-keys))

(defmethod emptyp ((x key-iterator))
  (null (keys-iterator-keys x)))

(defmethod current ((x key-iterator))
  (car (keys-iterator-keys x)))

(defun hash-keys (h)
  (loop :for key :being :the :hash-keys h
        :collect key))

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

при первом несовпадении элементов, возвращает ложь. Если несовпадающих нет, то истину.

Кстати, каков должен быть результат для коллекций разной «длины» и почему именно такой?

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

Очевидно, что type-of будет возвращать конкретный тип, но с помощью subtypep или какого-нибудь предиката а ля (satisfies (type-of x) 'port) мы можем узнать нужную информацию. Суть в возможности определения общих для всех подтипов port? методов (типа close) и вызова метода надтипа при отсутствии метода данного типа, например.

У типа метода быть не может. Даже в CL. Или покажи метод для (integer 0 10).

Если речь про класс, а не про тип, так в racket/class есть свой class-of.

Это проблемы не CLOS, а пакетной системы.

Да нет. Как раз CLOS. А точнее идеи, что класс не определяет пространство имён. Я делал костыль для обхода этого ограничения. Кроме того CLOS запрещает диспетчеризацию по количеству параметров. Если у одного класса есть метод с одним аргументом, то у другого класса я не могу сделать метод с этим же именем и двумя аргументами.

Да конечно, не пори чушь, такие методы можно размещать лямбдами в class-storage, например

Так type-of от этого не заработает.

А насчёт call, ты так говоришь, как будто send в Racket'овом ООП отменили

Какая разница? Ну пусть будет send.

Который, кстати, макрос, а не функция, в отличие от.

В отличие от чего? Есть dynamic-send, который функция. А send позволяет писать идентификаторы методов (они не являются значениями).

как он отличает произвольное замыкание от экземпляра какого-нибудь своего класса

В своём классе это замыкание будет внутри структуры instance.

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

Кстати, каков должен быть результат для коллекций разной «длины»

Ложь. Так как коллекции не совпадают. Согласен, что определение алгоритма математически не совсем корректно.

вообще я согласен, что в стандарте CL есть уродства, непонятно почему именно так сделанные

Основная проблема, что CL не даёт кирпичиков, позволяющих это исправить. Если в Racket я могу сделать свой отладчик вместо системного, могу сделать свой вариант многопоточности а-ля Erlang, могу сделать деструктор на объект, то в CL даже там где нужные вещи есть, они ограничены встроенными в компилятор.

Да и стиль программирования, принятый в Common Lisp, учит плохому

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

У типа метода быть не может. Даже в CL. Или покажи метод для (integer 0 10).

В CL нет диапазонных типов, (integer 0 10) — это как раз эти твои предикаты, с которыми каши не сваришь. Где-то была библиотека, дополняющая дженерики :filter-спецификатором, который позволял использовать подобные предикатные типы, но я не знаю как хорошо она работола жива ли ещё.

Да нет. Как раз CLOS. А точнее идеи, что класс не определяет пространство имён.

Он физически не может этого сделать, потому что CL не так работает с символами, чтобы это было возможно. Так что как раз CLOS тут не при чём.

Кроме того CLOS запрещает диспетчеризацию по количеству параметров. Если у одного класса есть метод с одним аргументом, то у другого класса я не могу сделать метод с этим же именем и двумя аргументами.

Это известное поведение, которое множество раз обсуждалось, в т.ч. и на ЛОР'е.

Так type-of от этого не заработает.

Как это? Каждый пользовательский тип — свой класс, type-of прекрасно работает.

Какая разница? Ну пусть будет send.

Да мне не важно название, важно, что ты это преподнёс вроде как минус («синтаксис (call ...)»), хотя в том же Racket посылка сообщения объекту осуществляется так же с помощью (send ...), причём в Racket send — макрос, а в CL call вполне может быть функцией, хотя ты жаловался на обилие макросов в CL и утверждал, что в Racket всё что можно реализовано функцией. Вопрос: почему send — макрос? почему with-input-from-file — макрос? При том, что он никаких синтаксических преобразований не делает и переписать его как функцию проще простого. P.S. Забавно, если я выполняю файл (буффер Ctrl+R), выдаёт ошибку, а в REPL — норм:

Добро пожаловать в DrRacket, версия 6.1.1 [3m].
Язык: racket; memory limit: 1024 MB.
with-output-to-file: unbound identifier;
 also, no #%app syntax transformer is bound in: with-output-to-file
> (pretty-print
 (syntax->datum
  (expand
   '(with-output-to-file "foo"
      (lambda ()
        (void))))))
'(let-values (((temp1) '"foo") ((temp2) (lambda () (#%app void))))
   (if (#%app
        variable-reference-constant?
        (#%variable-reference with-output-to-file56))
     (#%app with-output-to-file54 '#f '#f '#f '#f temp1 temp2)
     (#%app with-output-to-file56 temp1 temp2)))
Racket такой Racket.

могу сделать свой вариант многопоточности а-ля Erlang

Я, кстати, как-то сравнивал Racket треды и каналы с горутинами и гошными каналами и, насколько помню, Racket очень так не слабо всосал, а Go ведь даже не Erlang.

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

В CL нет диапазонных типов

Мы всё ещё про type-of?

* (type-of 2)

(INTEGER 0 4611686018427387903)

это как раз эти твои предикаты, с которыми каши не сваришь

CLOS на предикатах я делал: https://github.com/Kalimehtar/gls

(define (fact x) 
   (defgeneric fact0 
     (method ((n (and? <integer> (== 1))) (acc <integer>)) 
             acc) 
     (method ((n (and? <integer> (>/c 1))) (acc <integer>)) 
             (fact0 (- n 1) (* acc n)))
     (method ((n <integer>)) (acc <integer>)) 
             (error "Argument should be positive")))
   (fact0 x 1)) 

А точнее идеи, что класс не определяет пространство имён.

Он физически не может этого сделать, потому что CL не так работает с символами, чтобы это было возможно.

Может. Костыль я тебе уже показывал. Достаточно синтаксис вызова дженерика поменять на (send ...) и считать полным именем (в смысле CLOS) пару ((type-of arg0) . name) — это позволит именам методов не зависеть ни от чего снаружи класса.

Это известное поведение, которое множество раз обсуждалось

И ответ сводился к «это никому не нужно». А если нужно, то надо городить своё ООП. Хотя технических препятствий никаких.

Каждый пользовательский тип — свой класс, type-of прекрасно работает.

Только тип, введённый через defstruct/defclass. Вот есть https://github.com/andy128k/cl-gobject-introspection. Так на любой его объект type-of выдаст gir:object, а не реальный тип.

важно, что ты это преподнёс вроде как минус («синтаксис (call ...)»)

Не как минус, а как другой подход (не-CLOS).

почему with-input-from-file — макрос

Потому что тебе померещилось:

> with-input-from-file
#<procedure:with-input-from-file>

почему send — макрос

Потому что идентификатор метода — не значение. И есть аналогичная функция — dynamic-send. Преимущество макроса в том, что диспатчинг производится при компиляции.

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

Я, кстати, как-то сравнивал Racket треды и каналы с горутинами и гошными каналами и, насколько помню, Racket очень так не слабо всосал

Ссылку не найдёшь? Интересно, насколько.

Опять же, по той же методике с тредами SBCL сравнить...

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

Ссылку не найдёшь? Интересно, насколько.

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

Опять же, по той же методике с тредами SBCL сравнить...

Это уже не ко мне. =)

Возвращаясь к началу:

Например, написать функцию, которая получает функцию упорядоченного обхода коллекции (map-list, map-tree, ...) и две большие коллекции, затем сравнивает поэлементно содержимое этих коллекций, при первом несовпадении элементов, возвращает ложь.

Каким образом ты собрался упорядочивать хеш? И какой в упорядочивании (например ключей) хеша смысл?

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

Каким образом ты собрался упорядочивать хеш?

Сам же написал, по ключам.

И какой в упорядочивании (например ключей) хеша смысл?

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

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

Сам же написал, по ключам.

Но ты понимаешь, что в таком случае придётся не раз пройтись по коллекции ключей (1 раз в случае, если она хранится в хэше упорядоченной)? Т.е. я к тому, что создать список упорядоченных ключей отдельной функцией может быть вполне себе эффективным решением, кроме того такой вариант не блокирует хеш, используемый несколькими потоками.

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

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

Я уж не говорю о странности сравнения hasheq и hashequal.

По поводу тредов:

(define NCHANNELS 1000)
(define NTHREADS 10000)

(define channels (for/vector ((i (in-range NCHANNELS))) (make-channel)))

(define rand-gen (make-pseudo-random-generator))

(define (pick-channel)
  (let ((i (random NCHANNELS rand-gen)))
    (vector-ref channels i)))

(define (mixer)
  (define out (make-channel))
  (define n (- NCHANNELS 1))
  (define (loop)
    (let loop ((i 0))
      (when (channel-try-get (vector-ref channels i))
        (channel-put out #t))
      (loop (if (= i n) 0 (+ i 1)))))
  (thread loop)
  out)

(define (start-thread done)
  (thread
   (lambda ()
     (sleep 0.1)
     (channel-put done #t))))

(define (main)
  (define done (mixer))
  (for ((i (in-range NTHREADS)))
    (start-thread done))
  (for ((i (in-range NTHREADS)))
    (channel-get done))
  (displayln 'done))

(time (main))
-> Создать исполняемый файл -> Автономный (только для этого компьютера) -> База: Racket ->
done
cpu time: 1438 real time: 1438 gc time: 579

package main

import (
	"fmt"
	"math/rand"
	"time"
)

type signal struct{}

const (
	NCHANNELS = 1000
	NTHREADS  = 10000
)

var channels = func() []chan signal {
	c := make([]chan signal, NCHANNELS)
	for i := range c {
		c[i] = make(chan signal)
	}
	return c
}()

func pickChannel() chan signal {
	return channels[rand.Intn(NCHANNELS)]
}

func mixer() chan signal {
	return mixall(channels[0], channels[1:]...)
}

func startThread(done chan signal) {
	time.Sleep(time.Millisecond * 100)
	done <- signal{}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	start := time.Now()
	realMain()
	fmt.Println(time.Since(start))
}

func realMain() {
	done := make(chan signal)
	for i := 0; i < NTHREADS; i++ {
		go startThread(done)
	}
	for i := 0; i < NTHREADS; i++ {
		<-done
	}
	fmt.Println("done")
}

func mixall(ch chan signal, rest ...chan signal) chan signal {
	for _, ch2 := range rest {
		ch = mix(ch, ch2)
	}
	return ch
}

func mix(ch1 chan signal, ch2 chan signal) chan signal {
	out := make(chan signal)
	go func() {
		for {
			select {
			case msg := <-ch1:
				out <- msg
			case msg := <-ch2:
				out <- msg
			}
		}
	}()
	return out
}

->

done
177.2589ms

Разница почти в 10 раз.

Возможно тест в чём-то не корректен, скажи, что можно и/или нужно исправить?

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 2)
Ответ на: комментарий от monk

При смене параметров:

NCHANNELS = 100 (меньше каналов для большей конкуренции между тредами/горутинами)

NTHREADS = 100000

такие результаты:

Racket:

done
cpu time: 28000 real time: 27931 gc time: 12344

Go:

done
891.3125ms

Разница стала в почти 30 раз.

korvin_ ★★★★★
()
Последнее исправление: korvin_ (всего исправлений: 1)
Ответ на: комментарий от monk

Да, Go-код немного отличается от Racket, т.к. в последнем нет select, но, с другой стороны, в Go-коде создаётся чуть больше каналов и горутин (на микширование). В Racket это тупо циклическим перебором делается, возможно от этого и такая сильная разница. Попробуешь реализовать такой же select?

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