LINUX.ORG.RU

Лисп или Питон


0

4

Решил изучить новый язк программирования. Все думаю, чего лучше, лисп или питон. Цели - быстро делать мелкие поделки, небольшая гуйня, работа с субд типа скулайта, мускула, постгреса. Кто что подскажет, из знающих?

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

Вопрос был не совсем про то. Если функция aa вызывает функцию dd. Мы меняем _только_ dd, но не меняем aa. Что вернёт aa, после того, как мы изменим dd? Питон слил именно в этой точке, вернув старый вариант dd (если я не ошибаюсь, а я вроде бы в данном случае не ошибаюсь, хотя если я ошибаюсь, то поправьте меня).

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

> А чем плох такой код:

Он не просто длиннее, он засорён повторными упоминаниями имени типа. Для Вас это нормально, для меня - нет, давайте остановимся на том, что это вопрос вкуса. На самом деле, лишние упоминания - это плохо объективно. Вопрос лишь в том, считать _данные_ упоминания лишними или нет. Вы, наверное, их не считаете лишними, а я считаю и мы вряд ли договоримся (это уже проверено на трёх или четырёх лисперах, результат - 100%). Я считаю, что если функция в пределах 30 строк, то достаточно один раз написать тип каждого объекта, потому что если человек совсем не может запомнить тип переменной и ему за две строки нужно 4 раз это напомнить, то этот человек - или вообще не программист, или ему уже пора на пенсию. И лично я, если запомнил, что объект в этой переменной имеет тип my-struct, то меня раздражает, когда мне это напоминают ещё 4 раза. Если уж так нужно, чтобы название было говорящим, можно назвать переменную именем типа, что Вы и сделали. В этом случае писать имя типа при доступе к элементам структуры уже становится совсем абсурдным, пока у нас нет полиморфизма (а он есть далеко не всегда и при наличии полиморфизма ничто не мешает мне написать по-вашему).

Первый Ваш вариант ещё и хуже по производительности, поскольку при наличии декларации компилятор может сделать код более быстрым. Правда, тогда в моём коде нужно всё же заменить s.a.upcase на (string-upcase s.a).

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

А вот мне кстати интересно, что «лучше» - setf как в лиспе или указатели? С точки зрения производительности, надёжности и выразительности. Изначально кажется, что это лучше всего сделано в С++, где указатель - это, на самом деле, абстракция, которой можно придать произвольный смысл. Хотя и в лиспе вроде, если поворочаться немного (def-setf-method и, особенно, define-symbol-macro), становится так же просторно. Удивляет лишь то, что define-symbol-macro появился в языке так поздно.

Другое дело, что лисп двигает объекты и поэтому хороший «родной» (а не через FFI) код на лиспе никогда не догонит хороший код на С++.

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

> он засорён повторными упоминаниями имени типа

Не включай имя типа в имя accessor-а, часто именно так и надо делать, ведь создаваемые с defclass функции доступа это же обобщённые методы. И если для разных классов имеют смысл один и тот accessor (например, width), то он не должен содержаться имени типа. Это же реально очень важная фича.

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

> А вот мне кстати интересно, что «лучше» - setf как в лиспе

или указатели?


setf это обобщённое присваивание, а указатель тупо место в памяти (абстракция это итератор, а не указатель). Как ты вообще собираешься их сравнивать?

Другое дело, что лисп двигает объекты и поэтому хороший «родной»

(а не через FFI) код на лиспе никогда не догонит хороший код на


С++.



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


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

> а указатель тупо место в памяти
Я же написал С++, а не С.

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

Ты лазил по исходнику gethash в SBCL? Я лазил. Полазай, и многое станет ясно. Eq-хеш-таблица делается по адресу объекта в памяти. В С++ объект (по умолчанию) неподвижен. В лиспе (и любом языке, двигающем объекты при сборке мусора) все хеш-таблицы протухают после каждой сборки мусора и требуют перехеширования. В исходнике ты узнаешь, как в SBCL с этим пытаются бороться. И это - ещё не все причины, по которым язык со сборкой мусора может тормозить.

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

> Вопрос был не совсем про то. Если функция aa вызывает функцию dd. Мы меняем _только_ dd, но не меняем aa. Что вернёт aa, после того, как мы изменим dd?

proc aa {} {return [dd]}
proc dd {} {return old}

puts [aa] # возвращает old

proc dd {} {return new}

puts [aa] # возвращает new

результат:

old
new

Работает корректно и прозрачно, то, что доктор прописал :)

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

> Я же написал С++, а не С.

И что С++? В С++ и C понятие идентично. Как бы там ни было, указатели и setf не имею между собой ничего общего.

Ты лазил по исходнику gethash в SBCL?


Какая разница?

Я лазил


Круто, не всякий на это способен.

Полазай, и многое станет ясно.


О чём? О том, что автоматическое управление памятью не может быть более эффективным, чем ручное? Извините, подобными способностями делать общие выводы из частного примера не обладаю.

И это - ещё не все причины, по которым язык со сборкой мусора

может тормозить.



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

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

> Не включай имя типа в имя accessor-а
Да-да-да, вот именно отсюда и проистекает проблема, о которой я писал раньше. Не все поля width - обязательно родственники, и не всегда это accessor, иногда это может быть что-то другое. Поэтому, при таком подходе когда-нибудь (довольно быстро) нам понадобятся в одном пакете два width с несовместимой сигнатурой. Моё имхо состоит в том, что accessor должен называться как имя поля с суффиксом, например «width.» или width-of (width-of - это Ларс использует). В том коде, который я привёл, нет пока ветви для CLOS классов. Я ещё не придумал, во что расширять class-instance.something, потому что вариантов несколько. Это может быть slot-value, аксессор поля, полученный через MOP (наверняка ведь можно) символ class-type-something, или, допустим (something-of class-instance). Может быть, нужно сделать несколько синтаксисов, скажем, a.b будет вызывать аксессор, а a..b - slot-value.

Я избегаю пользоваться классами и буду ими пользоваться не раньше, чем будет полностью решена проблема разбиения кода на много мелких пакетов (может быть, сделать вообще, как в Перле - иерархия пакетов, конгруэнтная иерархии классов), поэтому данный вопрос пока что мало актуален. Пока обхожусь defstruct-ами или просто списками, иногда пользуюсь родовыми функциями. Слава Богу, синтаксис a.b теперь мне доступен (хотя и раньше был доступен, то, что я сделал вчера - это уже третья версия, надеюсь, что наилучшая).

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

это уже проверено на трёх или четырёх лисперах, результат - 100%

Вы прям как доктор диагнозы ставите :)

Как я понимаю речь идёт о доступе к полям классов/структур? Чтобы было по-короче? Тут есть варианты:

1) Прямые аккессоры как в моём посте выше.

2) with-slots или with-accessors:

(defstruct my-struct a b)

(let ((my-struct (make-my-struct :a "foo" :b "bar")))
  (with-slots (a b) my-struct
    (concatenate 'string (string-upcase a) b)))

так лучше?

3) Индивидуальный with-макрос, часто можно встретить (например в CFFI это практикуется):

(defstruct my-struct a b)

(defmacro with-my-struct ...)

(with-my-struct (my-struct (a "foo") (b "bar"))
  (concatenate 'string (string-upcase a) b))

так вообще хорошо.

4) Велосипеды c `->' или точечным доступом - издевательство над CL (настаиваю, что это не ИМХО - есть общепринятый стиль, если он не нравится - делайте, пожалуйта, свой язык/расширение, не курочьте CL :).

З.Ы. да, ещё archimag заметил ниже про conc-name у структур и reader/accessor у классов.

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

Я думаю, что это не «правильно» и не «не правильно» - это просто незначительные вещи. Короче, не то о чём приходтся думать большую часть времени.

Сама система структур/конструкторов/аккессоров в CL довольно продумана сама по себе и ещё может быть легко расширена до систем АТД/конструкторов/функций-доступа наподобие тех что в Haskell (другая продуманная система).

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

Прекрасно! tcl ставим большй знак «плюс».

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

> Да-да-да, вот именно отсюда и проистекает проблема,

о которой я писал раньше


Увы, пока суть проблемы я не уловил, а вот массу преимуществ использую на практике.

Не все поля width - обязательно родственники, и не всегда это

accessor, иногда это может быть что-то другое



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

Поэтому, при таком подходе когда-нибудь (довольно быстро) нам

понадобятся в одном пакете два width с несовместимой сигнатурой.



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

Я избегаю пользоваться классами


Ну вот, т.е. главных плюшек вы ещё не ели. Это вкусно. А defstruct это унылое устарелое, которым без лишней необходимости лучше не пользоваться.

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

with-slots или with-accessors

Ты сравнивал производительность slot-value и прямого акссера у defstruct? Я сравнивал. Разница до 5-кратной.

with-макрос

Да, на нём основаны предыдущие версии моего решения a.b. Вот оно (find-symbol-with-advanced-readtable-case надо заменить на просто find-symbol):

(defmacro with-conc-name (var conc-name &body body)
  "Теперь оно пытается игнорировать регистр символов "
  (let* ((svar (string var))
         (len (length svar))
         (string-concname (string conc-name)))
   (labels 
       ((conc-name-p (symbol) ; returns either nil or (values accessor-name var)
          (let ((pkg (symbol-package symbol)))
            (and 
             (or (eq pkg *package*)
                 (eq pkg #.(find-package :keyword)))
             (let* ((sname (string symbol))
                    (snamelen (length sname))
                    )
               (and 
                (> snamelen len)
                (eql (mismatch sname svar :test 'char-equal) len)
                (eql (elt sname len) #\.)
                (let ((accessor-name
                       (find-symbol-with-advanced-readtable-case
                        (string-upcase
                         (concatenate 'string 
                                      string-concname
                                      (subseq sname (1+ len))))
                        *package*
                        *readtable*
                        nil ; хм? FIXME разобраться, что значит этот nil. 
                        )))
                  (when accessor-name                                                       
                    (values accessor-name var))
                  ))))))
        (maybe-replace-var-with-conc-name (symbol)
          (multiple-value-bind (accessor-name var)
              (conc-name-p symbol)
            (if accessor-name
                `(,accessor-name ,var)
              symbol)))
        (maybe-replace-call-with-conc-name-call (call)
          `(maybe-replace-call-with-conc-name-call ,call)
          (destructuring-bind (symbol &rest args) call
            (cond ((symbolp symbol)
                   (multiple-value-bind (accessor-name var)
                       (conc-name-p symbol)
                     (if accessor-name
                         `(,accessor-name ,var ,@args)
                       `(,@call))))
                  (t `(,@call)))))
        (process-cdr (x)
          `(process-cdr ,x)
          (typecase x
            (null nil)
            (symbol (maybe-replace-var-with-conc-name x))
            (cons
             `(,(process-form (car x))
               ,@(process-cdr (cdr x))))))
        (process-form (x)
          `(process-form ,x)
          (typecase x
            (null nil)
            (symbol (maybe-replace-var-with-conc-name x))
            (cons
             (setf x (maybe-replace-call-with-conc-name-call x))
             `(,(process-form (car x))
               ,@(process-cdr (cdr x))))
            (t x))))
       `(progn ,@(loop :for form :in body :collect (process-form form)))
       )))


; TODO вспомнить про null object pattern
(defmacro let-with-conc-type (var type value &body body)
  "type must be an atom"
  (assert (typep type 'symbol) () "let-with-conc-type: ожидали имя типа, получили ~S" type)
  `(let ((,var ,value))
     (declare (type ,type ,var))
     (with-conc-name ,var ,(symbol+ type '-)
       ,@body)))

Но теперь я могу обойтись и без этого, когда производительность не нужна. Просто

>(defun f (x) (str+ x.a x.b)) 
>(f (make-my-struct :a "У" :b "рa"))
"Ура"
тоже будет работать. Вроде и лисп и уже почти как питон.

делайте, пожалуйта, свой язык/расширение, не курочьте CL

Меня моё решение устраивает, оно позволяет мне получать больше удовольствия от работы. А что думают пуристы - мне всё равно. Решением я готов делиться, кому надо, но ясно, что Ъ лисперы будут плеваться. Плюйтесь дальше. Я вас не зову на делёж моей зарплаты - она не настолько большая.

Короче, не то о чём приходтся думать большую часть времени

Это то, что приходится писать и читать большую часть времени, если программировать на лиспе «правильно». Суть алгоритма теряется за нагромождением этого бормотания и меня это просто бесило. Хорошо, что я с этим справился (сам себя не похвалишь - никто не похвалит).

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

> Работает корректно и прозрачно, то, что доктор прописал :)

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

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

> Думаю, это не то, что ему нужно, потому что аналогичный код в Питоне тоже работает
Ты в этом твёрдо уверен? Я проверял. Не работало. Ещё раз проверить, что ли, или поспорить с тобой на кило бананов?

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

Мне лень. Если ты живёшь в Москве, я готов поспорить на кило бананов. Условия: обе функции определены в файле и файл скомпилирован. А потом мы переопределяем dd интерактивно, а файл _не_ перекомпилируем.
Это предложение является офертой лично для tailgunner, при условии возможности встречи в Москве для передачи кило бананов.

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

Ты сравнивал производительность slot-value и прямого акссера у defstruct? Я сравнивал. Разница до 5-кратной.

Вот тут http://lisper.ru/forum/thread/186, и разницы не заметил. Во-вторых with-accessors не делает slot-value он связывает символы в symbol-macrolet - на производительность это никак не влияет. Это просто стандартный способ сделать сокаращение, как раз на тему «чтобы не бубнить одни и те же префиксы».

Разговор на тему «точечный доступ к слотам» (прозреваем сразу инфикс) это как разговор на тему "(+ 2 2) vs. (2 + 2)" (опять же, инфикс). Ради бога - есть расширения для второго, вы сделали для первого, хорошо хоть не проповедуете его как замену стандартной записи :)

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

Если ты живёшь в Москве


Не живу. Кроме того, мне не нужны бананы.

Python 2.5.2 (r252:60911, Jan 4 2009, 17:40:26)
[GCC 4.3.2] on linux2
Type «help», «copyright», «credits» or «license» for more information.

def aa(): return dd()

...

def dd(): return «old»

...

print aa()

old

def dd(): return «new»

...

print aa()

new

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

Хм. Бывает же... Ладно, проверю тогда сам насчёт функций в файле.

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

Страшно сказать, но кто-то из нас не понимает, как работает let. Я кое-что подозревал, но что до такой степени...

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

Страшно сказать, но кто-то из нас не понимает, как работает let.

Допустим, я не понимаю. Расскажите подробнее в чём тут дело? И за одно грамотный про всё это дело?

И в этой теме был правильный коммент, показывающий безграмотность теста:

Тот тест был про влияние деклараций типов и про поведение при различных способах доступа к слотам.

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

И за одно грамотный про всё это дело?

грамотный тест

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

> Ну не будем спорить, ваше отношение понятно. Но вы зачем-то начали разворачивать эту метафору - сравнение «стрелок и объектов» с «указателями и байтами», в исторической перспективе. Зачем? Разве что для красного словца, реально-то никакой связи. Первое - существующие архетектуры, второе - формализм который привлекают для описания программирования. Вот я и говорю - эти архетектуры никуда не делись, нужно писать какой-нибудь энтерпрайс на языках с жирными VM чтобы позволить себе забыть о архитектуре (ну либо на чистом ФП, в Haskell есть только _модели_ плоской памяти и указателей). А что касается второго - формализма - он сам по себе существует, независимо от программирования, его просто используют как инструмент описания (начали использовать математику для описания программирования - скатились обратно к байтам, это как?).

И указатели, и монады - это абстракции, которые по большому счету, не относятся в общем случае к problem domain, но которыми приходится оперировать при программировании на конкретном языке. Выросли ли эти абстракции из нижлежащей архитектуры или при попытке математически формализовать язык - неважно. Вопрос в том, удобно ли с помощью этих абстракций решать задачи, точнее с помощью каких удобнее. «Всё должно быть сделано настолько простым, насколько это возможно, но не проще». Чем проще абстракции - тем лучше, но не настолько простыми, чтобы это создавало проблемы при больших разработках, и не на столько сложными, чтобы это создавало проблемы для понимания. Должен быть баланс.

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

И указатели, и монады - это абстракции, которые по большому счету, не относятся в общем случае к problem domain

Указатель это просто число от 0 до 2^(32 или 64) - 1 лежащее в RAM или регистре - довольно конкретная вещь. Но к предметной области, точно, отношения не имеет. Что касается математики из теории типов - она имеет отношение к предметной области тогда, когда эта область - сами языки программирования (собственно, я больше ничего не утверждаю, только полезность математического языка в качестве мета-языка описания разных ЯП). Относительно того, имеют ли отношение «монады» к более конкретным problem domain - можно спорить. Дисциплина типов данных, ИМХО, весьма полезна.

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

А что по вашему является самыми простыми и мощными абстракциями? Может, объекты с изменяющимся состоянием и принимающие сообщения?

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

> Указатель это просто число от 0 до 2^(32 или 64) - 1 лежащее в RAM или регистре - довольно конкретная вещь.

Это адрес, а не указатель, не путай.

А что по вашему является самыми простыми и мощными абстракциями? Может, объекты с изменяющимся состоянием и принимающие сообщения?

Не знаю.

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

Это адрес, а не указатель, не путай.

А какая разница? Кроме того что указатель может иметь значение NULL - во всех остальных случаях его значение это адрес. А ссылка или тэгированный указатель просто хранят дополнительные данные помимо адреса.

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

> А что по вашему является самыми простыми и мощными абстракциями?

Пожалуй, абсолютно твёрдое тело и абсолютно чёрное тело, очень простые и мощные абстракции.

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

> А какая разница?

Указатель - это тип данных. А адрес - это идентификатор участка памяти в виде числа - то самое от 0 до 2^n. В си такой тип данных есть - можно складывать указатели с целыми числами, брать значение по адресу, функции выделения памяти возвращают указатели. В CL, например, такого или похожего типа нет, но при этом можно представить адрес в виде числа, что в CFFI и делается.

NULL

это 0. Он не имеет никакого специального значения.

А ссылка или тэгированный указатель просто хранят дополнительные данные помимо адреса.

Это внутри, снаружи ты этот адрес в portable-CL не узнаешь, потому что gc.

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

Указатель - это тип данных.

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

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

это 0. Он не имеет никакого специального значения.

Не 0 и имеет. Это топологический нуль (нейтральный элемент) в этом ТД ) Короче, особая точка. (Не байт по адресу «0», ведь правда?)

В CL, например, такого или похожего типа нет, но при этом можно представить адрес в виде числа, что в CFFI и делается.

Не совсем так - CFFI в этом месте это обвёртка над реализацией, а вот у реализации (диалект CL) адреса и указатели (настоящие) есть. В SBCL это SAP - для них определены операции сложения (адресная арифметика), они совместимы с malloc/free, и если речь идёт о foreign memory (без GC) - там это постоянные указатели.

(cffi:with-foreign-string (s "") (type-of s))
;=>
SYSTEM-AREA-POINTER

Т.е. в VM SBCL-я тоже есть такие персонажи (адреса и указатели). Хотя в CL как в языке определённом стандартом их действительно нет.

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

Ну, я всё равно не понимаю:

# это - файл test.py
def aa(): return dd()

def dd(): return "old"

и теперь:

>>> import test
>>> test.aa()
'old'
>>> test.dd()
'old'
>>> def test.dd(): return 'new'
  File "<stdin>", line 1
    def test.dd(): return 'new'
            ^
SyntaxError: invalid syntax
Сразу скажу, я в Питоне ламер, может, что-то не понимаю и это делается как-то по-другому?

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

ok, усложним пример:

proc ss {} {
 puts "вызов old"

 proc ss {} {
    puts "вызов new"
     proc ss {} {
     puts "вызов new2"
     return [puts "возврат из new2"]
    }
    ss
    return [puts "возврат из new"]
 }
 ss
 return [puts "возврат из old"]
}

ss

выхлоп:

вызов old
вызов new
вызов new2
возврат из new2
возврат из new
возврат из old

это уже «оно» ?))

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

> Тот тест был про влияние деклараций типов и про поведение при различных способах доступа к слотам.
Ну, тогда, возможно, это был правильный тест, но не про то. Я говорил не про вывод типов, а про скорость самой функции slot-value против structure-field. В среднем по самым продвинутым в плане компилятора и доступным мне реализациям (ccl, sbcl, lispworks, allegro) разница в разы. Я этот тест писать не буду, Вы можете мне верить, не верить или написать свой, в нём акссессор к полю структуры должен вызываться во внутреннем цикле. Хотя, кстати with-accessors может использовать прямой доступ в случае структур. Но это неважно, всё равно конструкция лишняя. Компилятор знает, что есть структура, знает, какие в ней есть поля, нафига их опять перечислять?

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

Да, оно. И будет совсем «оно», когда ss можно будет переопределить ещё и интерактивно, при том, что она сама определена в «библиотеке», если в tcl есть такое понятие. Почему-то я верю, что это возможно в tcl, и всё ещё не убеждён, что это возможно в Питоне.

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

> Тебе должен понравиться очень лаконичный clojure
ИМХО JVM со всеми этими JIT - это хитрая придумка для развода на бабло.
Clojure был бы ничего, если бы не склонность к чистому ФП. И ридер они порезали слишком сильно, хотя, может уже обратно пришили, я не слежу. В целом, конечно же, базовый синтаксис clojure более вменяемый, чем CL. Но CL - это компилятор в нативный код, а clojure - в JVM.

монады

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

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

> def test.dd(): return 'new'

Ыыы. Попробуй так:

def new_dd(): return «new»

test.dd = new_dd

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

from test import dd

dd - это новый символ, определенный в текущем модуле и, если ты хочешь его переопределить, надо делать:

dd = new_dd

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

> Разговор на тему «точечный доступ к слотам» (прозреваем сразу инфикс) > это как разговор на тему "(+ 2 2) vs. (2 + 2)"
2+2 лучше, конечно. Но это не так болезненно. Мне нужно именно избавиться от бормотания, а заодно и от лишних скобок. Разница между
(acсessor object field) и прсто object.field для меня весьма существенна, т.к. позволяет, приняв весьма простые соглашения, сделать код намного лаконичнее.

Общая инфиксная запись в контексте лиспа вряд ли прокатит, потому что она будет хрупкой (если учесть квазицитирование и macro-charaters). Преимущества у неё, конечно, есть, поскольку она позволяет за счёт приоритетов иногда писать меньше скобок (и иногда наступать на грабли). Но без неё, в принципе, жить вроде можно. А вот скобки разнообразить, как в clojure, было бы не лишним, хотя тут надо ещё подумать, как не помешать разным расширениям.

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

Без проблем.

Допустим, мы взяли jpeg из tcllib
http://tcllib.sourceforge.net/doc/jpeg.html

далее, подключаем модуль jpeg из tcllib:

package require jpeg

а тут уже есть варианты ))

вариант 1
можно сразу просто вырубить встроенный компонент в jpeg
например:
rename ::jpeg::debug «»


вариант 2
просто переопределить один компонент
proc ::jpeg::debug {} {
puts hello
}

::jpeg::debug

можно сочетать оба варианта по потребностям
и это все будет работать и в итерактивном режиме.

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

А, да, я что-то смутно начинаю припоминать. В общем, пока что это выглядит как полный отстой. Ведь новое определение будет в моём модуле, а не в модуле test, значит, я не могу взять старое определение, подправить его, применить и запихать обратно в модуль test.
Может быть, можно что-то вокруг этого накрутить на уровне среды, но пока примера этого я не видел. И даже такая задача вроде не ставится.

В лиспе же я нажимаю M-., попадаю на исходник функции. Меняю его, жму ctrl-shift-c и всё, функция изменена. Если работает - сохраняю файл. То же для классов и пакетов. Со макросами сложнее, и особенно жалко, что это не катит для структур. Собственно, ради одной возможности менять функции и классы в рантайме уже стоило бы терпеть все гадости лиспа, потому что это - реальная интерактивная разработка, которая экономит просто океаны времени. Среда, в которой этого нет - это отстой и потеря времени (и таковы почти все современные среды разработки). Разработчик такой среды - ламер, потому что в те времена лисп ещё преподавали в вузах и он обязан был знать.

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

> В общем, пока что это выглядит как полный отстой.

Пока что выглядит, что ты просто не знаешь Питон.

Ведь новое определение будет в моём модуле, а не в модуле test, значит, я не могу взять старое определение, подправить его, применить и запихать обратно в модуль test.

Сможешь. Я даже показал, как именно.

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

Вы можете мне верить, не верить или написать свой, в нём акссессор к полю структуры должен вызываться во внутреннем цикле

Старый добрый dotimes? :)

Вот slot-value:

(defstruct foo a b)

(disassemble
 #'(lambda () (let ((s (make-foo :a 1 :b 2)))
                (slot-value s 'a))))
; disassembly for (LAMBDA ())
; 0B78DC2E:       8D5C24F8         LEA EBX, [ESP-8]           ; no-arg-parsing entry point
;       32:       83EC10           SUB ESP, 16
;       35:       8B15F8DB780B     MOV EDX, [#xB78DBF8]       ; :A
;       3B:       BF04000000       MOV EDI, 4
;       40:       8B35FCDB780B     MOV ESI, [#xB78DBFC]       ; :B
;       46:       C743F808000000   MOV DWORD PTR [EBX-8], 8
;       4D:       8B0500DC780B     MOV EAX, [#xB78DC00]       ; #<FDEFINITION object for MAKE-FOO>
;       53:       B910000000       MOV ECX, 16
;       58:       892B             MOV [EBX], EBP
;       5A:       8BEB             MOV EBP, EBX
;       5C:       FF5005           CALL DWORD PTR [EAX+5]
;       5F:       8B5207           MOV EDX, [EDX+7]
;       62:       8BE5             MOV ESP, EBP
;       64:       F8               CLC
;       65:       5D               POP EBP
;       66:       C3               RET
;       67:       CC0A             BREAK 10                   ; error trap
;       69:       02               BYTE #X02
;       6A:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       6B:       4F               BYTE #X4F                  ; ECX

вот прямой аккессор:

(disassemble
 #'(lambda () (let ((s (make-foo :a 1 :b 2)))
                (foo-a s))))
; disassembly for (LAMBDA ())
; 0B8D6AF6:       8D5C24F8         LEA EBX, [ESP-8]           ; no-arg-parsing entry point
;      AFA:       83EC10           SUB ESP, 16
;      AFD:       8B15C06A8D0B     MOV EDX, [#xB8D6AC0]       ; :A
;      B03:       BF04000000       MOV EDI, 4
;      B08:       8B35C46A8D0B     MOV ESI, [#xB8D6AC4]       ; :B
;      B0E:       C743F808000000   MOV DWORD PTR [EBX-8], 8
;      B15:       8B05C86A8D0B     MOV EAX, [#xB8D6AC8]       ; #<FDEFINITION object for MAKE-FOO>
;      B1B:       B910000000       MOV ECX, 16
;      B20:       892B             MOV [EBX], EBP
;      B22:       8BEB             MOV EBP, EBX
;      B24:       FF5005           CALL DWORD PTR [EAX+5]
;      B27:       8B5207           MOV EDX, [EDX+7]
;      B2A:       8BE5             MOV ESP, EBP
;      B2C:       F8               CLC
;      B2D:       5D               POP EBP
;      B2E:       C3               RET
;      B2F:       CC0A             BREAK 10                   ; error trap
;      B31:       02               BYTE #X02
;      B32:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;      B33:       4F               BYTE #X4F                  ; ECX

нет разницы - потому что в sbcl/src/pcl/fixup.lisp

(deftransform slot-value ((object slot-name) (t (constant-arg symbol)) *
                          :node node)
  (let ((c-slot-name (lvar-value slot-name)))
    (if (sb-pcl::interned-symbol-p c-slot-name)
        (let* ((type (lvar-type object))
               (dd (when (structure-classoid-p type)
                     (find-defstruct-description
                      (sb-kernel::structure-classoid-name type))))
               (dsd (when dd
                      (find c-slot-name (dd-slots dd) :key #'dsd-name))))
          (cond (dsd
                 `(,(dsd-accessor-name dsd) object))
                (t
                 (delay-ir1-transform node :constraint)
                 `(sb-pcl::accessor-slot-value object ',c-slot-name))))
        (give-up-ir1-transform "slot name is not an interned symbol"))))

видим, что в нашем случае статической структуры и известного имени слота производится трансляция времени компиляции:

(slot-value s 'a) ;=> (foo-a s)
;; just because

Что касается with-slots и with-accessor:

(macroexpand-1
 '(with-accessors ((a foo-a) (b foo-b)) s
    (list a b)))

(LET ((#:G899 S))
  (DECLARE (IGNORABLE #:G899))
  (DECLARE (SB-PCL::%VARIABLE-REBINDING #:G899 S))
  #:G899
  (SYMBOL-MACROLET ((A (FOO-A #:G899)) (B (FOO-B #:G899)))
    (LIST A B)))

(macroexpand-1
 '(with-slots (a b) s
    (list a b)))

(LET ((#:G900 S))
  (DECLARE (IGNORABLE #:G900))
  (DECLARE (SB-PCL::%VARIABLE-REBINDING #:G900 S))
  #:G900
  (SYMBOL-MACROLET ((A (SLOT-VALUE #:G900 'A)) (B (SLOT-VALUE #:G900 'B)))
    (LIST A B)))

в свете вышесказанного - тут нет никакой разницы (что в том древнем тесте тоже было видно). Конкретная разница может появится для классов и в более сложных случаях.

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

Эта фича:

commit 796873d7b696e1079d2319844444040d18e0e2b1
Author: Nikodemus Siivola <nikodemus@random-state.net>
Date:   Thu Aug 6 15:57:26 2009 +0000

    1.0.30.40: faster SLOT-VALUE on structures
    
     * Replace the SLOT-VALUE and SET-SLOT-VALUE compiler macros
       with deftransforms once PCL has been built, and if the type
       is known to be a structure and the slot name maps cleanly
       to an accessor we can use it.

так что вы туда смотрели уже больше года назад по крайней мере :)

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

2+2 лучше, конечно.

Согласен. Решателям SLAE стоит взять какой-нибудь infix.lisp из репозитория CMU (а то добро пропадает) и поместить это дело под macro-character. А так, язык арифметики и булевой логики в обычных программах - редкость, всё больше пакеты/классы/специальные формы вроде определений верхнего уровня и прочих из consrol flow - там нет инфикса в принципе, везде префикс (как и в других языках).

Разница между (acсessor object field) и прсто object.field для меня весьма существенна

Ъ оппортунисты негодуэ, же? Зачем писать код который будет тянуть за собой эти convensions - его будет трудно использовать в остальных программах, в которых придерживаются стандартных для CLOS аккессоров.

А вот скобки разнообразить, как в clojure, было бы не лишним, хотя тут надо ещё подумать, как не помешать разным расширениям.

Ну тут named-readtables at common-lisp.net - чтобы избегать подобных конфликтов.

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

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

Я даже показал, как именно.

Ты показал хак, который можно применять в каких-то особых случаях. А мне нужна технология редактирования кода в рантайме. В лиспе это делается легко и спокойно. Я тебе объяснил, чем (с моей ламерской точки зрения) этот хак плох. Тем, что в текущем модуле другой набор доступных идентификаторов. Мне же не нужно просто заменить функцию на другую. Мне обычно нужно редактировать функцию, которая уже есть (допустим, в ней нашёлся баг или просто нужно её расширить). И я хочу это делать, не выходя из программы, чтобы сэкономить время на перезапуске. И не надо мне объяснять, что мне это не нужно, потому что я точно знаю, что мне это нужно и, более того, я этим постоянно пользуюсь в лиспе.

И вот, пусть в модуле test имеется хитрый import. Я взял исходник из модуля test, скопировал его в командную строку. Это уже другой исходник, потому что те же идентификаторы могут в нём иметь другой смысл. И то, что я получу в качестве новой функции, будет содержать не только тот баг, который я хотел исправить, а и ещё кучу новых. ИЛи вообще не скомплируется. В этом и состоит отсутствие технологии (само-то присваивание можно сделать средствами среды). Итого: я не могу технологично отредактировать функцию в рантайме. Пока не могу. Питона я не знаю. Но я в своё время пробил этот вопрос и пришёл к выводу, что такой технологии, к в лиспе, у питона нет. Может быть, плохо пробил, но пока мои выводы представляются верными.

Если в питоне можно перейти в контекст модуля test, тогда, похоже, что мне придётся взять свои слова назад. Но из твоих примеров этого не видно. И, я помню, вроде есть какая-то тонкость, отличающая def и присваивание. В каких-то случаях они вроде будут вести себя по-разному, так что это не переопределение в чистом виде (хотя тут я могу и заблуждаться).

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

Да, больше года назад. Возможно даже, что они это сделали после того, как я на c.l.l. написал о результатах своих тестов. Кстати, SBCL - это не единственная реализация лиспа.

У меня

> 'foo.a.b
#:\(-->\ \(-->\ foo\ a\)\ b\)

> describe *
#:\(-->\ \(-->\ foo\ a\)\ b\) is a SYMBOL
NAME          "(--> (--> foo a) b)"
VALUE         #<unbound value>
FUNCTION      #.#'(LAMBDA
                      (DSPEC::%%MACROARG%% #:&ENVIRONMENT34330 &AUX (#:&WHOLE34331 DSPEC::%%MACROARG%%)
                       (#:\(&REST\ ...\)34332 (CDR #:&WHOLE34331))
                       (#:CHECK-LAMBDA-LIST-TOP-LEVEL34333
                        (DSPEC::CHECK-LAMBDA-LIST-TOP-LEVEL '(&REST #:args)
                                                            #:&WHOLE34331
                                                            #:\(&REST\ ...\)34332
                                                            0
                                                            0
                                                            'T
                                                            :MACRO))
                       (#:args #:\(&REST\ ...\)34332))
                    (DECLARE (HARLEQUIN-COMMON-LISP:LAMBDA-LIST &REST #:args))
                    (BLOCK #:\(-->\ \(-->\ foo\ a\)\ b\) `(--> #:\(-->\ foo\ a\) "b" ,@#:args)))
PLIST         (SYSTEM::PC-SYMBOL-MACRO-DEFINITION
               (--> #:\(-->\ foo\ a\) "b")
               PKG::SYMBOL-NAME-STRING
               "(--> (--> foo a) b)")
PACKAGE       NIL
хех 8^) Хотя сегодня я уже думаю, что это немного излишне. Можно было сразу расширять в (--> (--> foo «A») «B»), я просто недотумкал, как надо ещё от$@#$%$ть reader. Но пока работает и так, пусть себе работает.

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

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

Да в стандарте CLOS нет соглашений об именах акссесоров, нет их, как я понял, и в культуре лисперов. И по-моему, это плохо.

В общем-то, я не планирую использование своего кода в каких-то гипотетических «других программах». Ъ лисперы такой код не одобряд, да и этих-то Ъ лисперов полтора человека на весь мир. Скорее всего, либо я буду и дальше писать свои проекты сам, либо под меня будут подстраиваться, если мне удастся разрастись до того, как я смогу кого-то нанять (это маловероятно, впрочем). Я уже написал и активно пользую несколько вещей, которые Ъ никогда не признают, так что пропасть уже есть и обратно она уже не срастётся. Даже невинное

(defmacro let1
(variable + &body progn)
"Shortcut for (let ((a b)) . c) or (destructuring-bind a b . c)"
(if (atom variable)
`(let ((,variable ,+)) ,@progn)
`(destructuring-bind ,variable ,+ ,@progn)))
и то вызывает у некоторых отторжение. Я не лиспер, да. У меня нет скобкофилии.

Поскольку код, раскрывающий a.b для классов, пока не существует, его можно сделать произвольным. Например, завести несколько способов для разных иерархий и ассоциировать их с базовыми классами этих иерархий - тогда я смогу прикрутить свой синтаксис к чужим CLOS библиотекам. Впрочем, я сильно сомневаюсь, что это когда-нибудь понадобится, потому что как-то пока без этого удавалось обойтись.

Ну тут named-readtables at common-lisp.net - чтобы избегать подобных конфликтов

Не могу сказать, что я допилил их до комфортного состояния, хотя пользуюсь уже года два. Самым правильным оказалось просто привязывать ридмакросы к символам, а не к буквам. Потому что букв слишком мало и на всё не хватит. При привязки ридмакросов к символам, вопросы слияния и импорта решаются очень легко и просто и становится достаточно одной таблицы чтения на всё. В общем-то, 90% времени я провожу в одной таблице чтения.

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