Портировать Яро-раскладку на Debian
https://budden73.livejournal.com/27328.html
Цена полного решения - 20 тыр.
Полное решение должно содержать исчерпывающие инструкции о том, как добиться работоспособности раскладки со всеми свистелками и т.п.
https://budden73.livejournal.com/27328.html
Цена полного решения - 20 тыр.
Полное решение должно содержать исчерпывающие инструкции о том, как добиться работоспособности раскладки со всеми свистелками и т.п.
Я хочу следующие исправления/улучшения в SBCL:
Некорректная работа вывода типов
(defparameter *g* (cons 1 2))
(defun g () (setf (car *g*) 'a))
(defun f (x)
(when (typep x '(cons fixnum))
(g)
(if (typep x '(cons fixnum))
"Still cons fixnum!"
"Not cons fixnum")))
(f *g*)
Нужно внести в язык и в реализацию понятие о настоящем и ненастоящем типе и сделать так, чтобы вывод типов не использовал ненастоящие типы (или использовал их настоящую часть, в данном случае cons). При этом будет установлено, что defstruct-ы и deftype всегда будут переопределяться только вместе с unintern того символа, который их именует, за счёт этого настоящих типов станет гораздо больше.
Декларация const Нужно её как-то воплотить и использовать в системе типов. Соответственно, если объект const, то его можно передавать только в функцию, которая принимает const. Для структур, массивов и консов const распространяется только на первый уровнь ссылок (или рекурсивно? я не знаю, как надо сделать). Для классов CLOS - не нужно ничего делать, они меня не интересуют и я ими не пользуюсь.
Запрет неявного сужения типов Добавить новое качество оптимизации (к speed, debug и т.п.). Если это качество включено, то присваивание месту, декларированному как integer, типа number порождает warning или compilation error.
Мне нужен чистый форк и внесение изменений в реализацию, а не просто патчи. Патчами можно сделать далеко не всё. Примут его или нет, как его поддерживать потом - это всё на данном этапе неважно. Нужно решить техническую задачу.
Если найдутся добровольцы, то я только за. Напоминаю, что анонимусы у меня в игноре, так что анонимусы могут в тему вообще ничего не писать :) И денег хрен заплачу :)
Вопрос 1
есть два интерфейса очереди. Одна, допустим, очередь аудио буферов, а другая - очередь видеобуферов. Они разработаны разными поставщиками и находятся в разных (чужих для меня) пакетах.
Я пытаюсь написать видео плейер со звуком, и мне нужно реализовать в одной структре оба интерфейса. У каждого из них есть метод Извлечь() без параметров.
В C# это сделать можно, явно указав имя интерфейса, к-рый я реализую. В С++ при множественном наследовании (не ромбовидном) тоже можно (хотя бы в каких-то контекстах) указать, от какого предка я беру метод Извлечь. В Eiffel, если уж зашла о том речь, можно переименовать методы при наследовании. В CLOS родовые функции живут в разных пр-вах имён.
Мои действия в golang?
Вопрос 2 (он про Яр, конечно же)
Есть билиотека ::словари::, в ней есть тип контейнер, и у него метод контейнер%дай(строка). По соглашению, если ц - типа контейнер, то ц.дай превращается в контейнер%дай(ц). Т.е. примерно то же, что утиная интерфейсизация в golang, только интерфейс в данном случае состоит из одной функции. Я это придумал никак не глядя на голанг, а всего лишь пытаясь сделать костыль для неудобного обращения в лиспе к полям структур.
В библиотеке ::словари:: есть также generic код:
опр функ Напечатай-все-значения(о) тело
для каждого ключа к (о) // мифический синтаксис
л-печать(о.дай(к))
кнд кно
Но это - мина, которая позже рванёт.
Также есть (чужая) библиотека «рефлексия», в ней есть тип рефлексирующий-субъект, у которого тоже есть рефлексирующий-субъект%дай(строка), но по смыслу это означает «дай метаинформацию такого-то типа».
И теперь мы делаем рефлексивный словарь. И оказываемся в тупике, т.к. нам нужно сделать две функции рефлексивный-словарь%дай с разным смыслом.
Второй пример я привёл для того, чтобы оценивать жизненность предлагаемых решений в golang. Аналогия не полная, но всё же нужно иметь в виду, что мы можем отдавать наш объект и в тот код, который написан авторами интерфейса и который не может ссылаться на переименованный метод. Насколько подходит вариант с делегированием интерфейсов полям структуры - я не совсем понял, вот и разберёмся в этой теме.
Просмотрел три статьи про вариантность, во всех неточности или ошибки:
https://ru.wikipedia.org/wiki/Ковариантность_и_контравариантность_(программир...
Статья неверна в принципе, начиная с определений:
Ковариа?нтность и контравариа?нтность[1] в программировании — способы переноса наследования типов на производные[2] от них типы — контейнеры, обобщённые типы, делегаты и т. п.
Ковариантностью называется сохранение иерархии наследования исходных типов в производных типах в том же порядке.
Если мы просто передаём параметр, List<Cat> там, где от нас ждут List<T extends Animal>, то мы можем подставить List<Cat> «вместо» List<Animal>, но при этом List<Cat> не становится потомком List<Animal>. Т.е., иерархии никакой нет, а есть заменимость типов в конкретном контексте.
Далее, если мы просто пишем Animal *А = new Cat(), то я бы это тоже рассмотрел как ковариантность, хотя здесь нет никаких производных иерархий. Точно так же, здесь есть подстановка одного вместо другого, я бы назвал это «тривиальным» или «единичным» случаем вариантности. Хотя, конечно, о терминах не спорят - их просто принимают. Но если мы говорим о натуральных числах, то логичнее включить в определение единицу, а не начинать их с двойки.
http://habibutsu.github.io/variance.html - для С++ ошибка в том, что говорится «нужно было С», или «нужно было А», в то время как правильно будет «нужно было B или C» или «нужно было B или А»
http://boyarincev.net/2014/09/covariance-and-contrvariance-in-c-sharp/
В контравариантности путаница (неправильно объяснено).
И т.д.
Вопрос: где же найти правильную статью про ковариантность, но без теорката (из моих трёх статей одна - с теоркатом, но её это не спасло от фактических ошибок). И заодно, предлагаю своё определение:
**********************
Вариантность - это возможность использовать подтип Пт вместо супертипа Ст или наоборот, в ряде частных случаев:
Т.е., получается, что отношение между типами зависит не только от самих типов или от их родственности (Конт<Т> могут быть друг другу не родственны или родственны странным образом через Object), но и от контекста применения
Инвариантность - это просто отсутствие ко- или контра-вариантности, т.е. отсутствие предмета обсуждения как такового.
**********************
Прошу проверить правильность моего определения и сказать, все ли случаи применения вариантности в реальных ЯП я перечислил. Про Хаскель пока не спрашиваю, т.к. до функторов ещё не дочитал :) Но можете и про него написать :)
А вот предыдущая тема про это: Ковариантность и динамическая типизация
В частично упорядоченном множестве хочу быстро узнавать, верно ли, что А<Б. Видимо, для этого надо построить какой-то индекс и пользоваться им. Какой? Как его быстро построить?
Пока нашёл вот это: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.6760&rep=rep1... но даже не смотрел. Мне кажется, это должно быть в каком-то учебнике, который я не читал.
Например, аналог Map<T1,Т2> с интеграцией в систему типов SBCL, чтобы можно было выкинуть проверки, когда тип известен во время компиляции?
Согласно принципу Лисков, экземпляр подтипа должен вести себя так же, как экземпляр супертипа. И вот у нас Keyboard - подтип Device, мы делаем какой-нибудь List<Device> (как бы супертип) и List<Keyboard> (как бы подтип).
Но на самом деле нет. Если мы рассмотрим полную сигнатуру функции «добавить» с учётом динамической типизации, то оказывается, что попытка вставить Mouse в List<Keyboard> имеет определённое поведение, отличающееся от такового для List<Device>. В первом случае будет ошибка времени выполнения (некорректный тип), а во втором мышь вставится в список.
Т.е. здесь нет ковариантности.
А теперь вопрос: что используется в ковариантности в языках, позволяющих скомпилировать код, вставляющий «нечто» (any,variant,t) в такой вот «псевдоковариантный» контейнер.
Мне кажется, должен быть какой-то костыль для этой ситуации, и я подозреваю, что он может присутствовать в Scala и/или в C#. Кто может что-нибудь сказать?
Было бы интересно сравнить производительность вирт. функций.
Нужно учесть следующее: доступ к полям в виде родовых функций намного-намного медленнее доступа к полям структуры, за исключением частных случаев (например, в теле метода, специфицированного этим классом, может быть лишь раза в полтора медленнее структуры, согласно рук-ву SBCL). Кроме того, в С++ есть статические методы, к-рых в CLOS вроде бы как нет. Далее, я не смог заставить CLOS заинлайнить вызовы вирт.ф-й, а в С++ это в принципе возможно (хотя я не понял, заинлайнены ли они в нашем случае).
По этой причине я взял вместо классов структуры (и в жизни тоже всегда использую структуры вместо классов).
Код такой, но за качество не ручаюсь. Добровольцы на code review приглашаются.
;; clos-virtual-method-benchmark.lisp
(in-package :cl-user)
(declaim
(optimize (speed 3) (safety 0) (debug 0)
(space 0) (compilation-speed 0)))
(proclaim
'(optimize (speed 3) (safety 0) (debug 0)
(space 0) (compilation-speed 0)))
(defparameter *times* (* 100 1000 1000))
(defstruct base
next
)
(defstruct (test-class-3 (:include base))
fld1
fld2
)
(declaim (sb-ext:maybe-inline Делишко+))
(proclaim '(sb-ext:maybe-inline Делишко+))
(declaim (inline Делишко+))
(proclaim '(inline Делишко+))
(defgeneric Делишко+ (ц))
(defmethod Делишко+ ((ц base))
7)
(defmethod Делишко+ ((ц test-class-3))
(setf (test-class-3-fld1 ц) (test-class-3-fld2 ц))
1)
(defmethod Делишко+ ((ц string))
0)
(defun inner (o n)
(declare (type fixnum n))
(declare (type base o))
(declare (inline Делишко+))
(let ((res 0))
(declare (type fixnum res))
(dotimes (i n)
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(incf res (the fixnum (Делишко+ o)))
(setf res (mod res 16))
(setf o (base-next o)))
(print res)
res))
(defun main()
(let* (
(o1 (make-test-class-3 :fld1 1))
(o2 (make-test-class-3 :fld1 1 :next o1))
res)
(setf (test-class-3-next o1) o2)
(setf res (inner o1 *times*))
(format t "~%~S~%" res)))
(let ((*trace-output* *standard-output*))
(time (main)))
;; eof
С++
// cpp-virtual-method-benchmark.cpp
#include "stdio.h"
#include <cstdlib>
class base_class {
public:
base_class* next;
virtual int meth() { return 0; };
};
class test_class : public base_class {
public:
int fld1, fld2, fld3, fld4, fld5;
int meth () { fld4 = fld2; return 1; };
};
int inner(base_class *o,int bound) {
int res=0;
for (int i=0;i<bound;i++) {
res += o->meth();
res += o->meth();
res += o->meth();
res += o->meth();
res += o->meth();
res += o->meth();
res += o->meth();
res += o->meth();
res += o->meth();
res += o->meth();
o = o->next;
res = res % 16;
}
return res;
}
int main(int argc, char* argv[])
{
test_class o1;
test_class o2;
o1.fld1=1;
o2.fld1=1;
o1.next=&o2;
o2.next=&o1;
int n = 100*1000*1000;
int result=inner(&o1,n);
printf("%d %d\n",o1.fld5,result); // проверяем корректность и чтобы оптимизатор
// не выкинул неиспользуемый код
return 0;
}
// g++ -O2 -o cpp-benchmark cpp-virtual-method-benchmark.cpp ; echo disassemble inner | gdb cpp-benchmark ; time ./cpp-benchmark
Ну и для полноты картины, создаваемая объектная система для Яра:
Получилось в 1.6 раза быстрее CLOS и в 2.7 раз медленнее С++. Правда, для множественной диспетчеризации скорее всего проиграем против CLOS. С другой стороны, сделать inline статический диспатч для CLOS методов в случае, когда тип достаточно хорошо известен, мне не удалось, оно делается с помощью MOP. Я делаю примерно то же, но без CLOS.
Вот мне всегда казалось, что кортежи - это лишнее понятие. Сейчас копаюсь в потрохах SBCL и вижу, насколько они усложняют вывод типов и систему типов. Скажем так, более чем вдвое. В Си можно можно просто определить struct. Можно его инициализировать двумя значениями в красивой форме
struct gasket {
float weight;
unsigned height;
unsigned diameter;
};
void main() {
struct gasket obj = { 12.f, 120, 30 };
Можно вернуть структуру из функции, можно передать в функцию. Спрашивается, а зачем тогда кортежи? Что нового они вообще дают хоть в каком-то языке? Понятно, что в языках, где список может состоять из элементов только одного типа, и где нет типа any/t/variant, кортежи могут быть кривым костылём для полиморфных списков. Ну и теоретически они могут быть эффективнее. Но Си всё равно даёт то же самое, плюс поля кортежа ещё и именованные. Единственное неудобство - это то, что тип структуры нужно объявлять. Но в этом можно видеть плюс, т.к. объявление структуры заставляет дать вещам имена и тем самым сделать программу более явной и понятной. А типизация полей структуры убережёт от ошибок.
Единственное, что я заметил - так это то, что времена жизни, константность и прочие атрибуты объектов в кортеже никак не связаны, а в структуре - связаны. Но если структура наполнена указателями, то и это почти не имеет значения.
Что я упустил?
Как написать вот это:
зайти-в-сарай()
try
ткнуть-палкой-в-осиное-гнездо-в-сарае()
catch all
вернуть "Ой"
end
и вот это:
зайти-в-сарай()
try
ткнуть-палкой-в-осиное-гнездо-в-сарае()
finally
уходя-из-сарая-погасить-свет()
end
И вообще, есть ли где-нибудь гайд по хаскелю для тех, кто приходит с других языков, который бы не начинался со слов «оставь своё эго за порогом, когда приходишь в мир хаскеля».
Это не предложение о работе. Это просто вопрос. Прежде всего, вопрос обращён к тем, кто знает лисп, а не к школьникам, которые просто хотят много зарабатывать.
Перемещено jollheef из job
https://github.com/cbaggers/varjo
1500 коммитов за 4 года, два контрибюьтора.
Остальное не понял.
> (ql:quickload :varjo)
Помните, я сказал, что постараюсь не писать до конца сентября? Так вот: у меня не получилось :) Кому не нравится тут тег Яр - заверяю, что он не для рекламы (и так все уже знают), а исключительно для удобства: мне по нему удобно искать, а вам его удобно заигнорить. Отношение к Яру здесь такое: я мучаюсь, как мне реализовать объекты Яра. Склоняюсь к тому, что сделать их с опцией «реализовать как defstruct/clos». Но дальше идут тонкости, в которые сейчас не лезу.
Итак, я нашёл тред пиписькометрии 7 - летней давности: https://www.linux.org.ru/forum/development/4535326, и там сравнивалась скорость каких-то циклов. но это неинтересно. Интересно сравнивать скорость доступа к полям объектов хотя бы. Я попробовал. Вот что в итоге вышло (платформа 64 разрядная).
С++
// cpp-benchmark.cpp
#include "stdio.h"
#include <cstdlib>
class test_class {
public:
int fld1, fld2, fld3, fld4, fld5;
test_class *next;
};
int inner(test_class *o,int bound) {
int res=0;
for (int i=0;i<bound;i++) {
res += o->fld1;
o->fld2 = o->fld1;
o->fld3 = o->fld2;
o->fld4 = o->fld3;
o->fld5 = o->fld4;
o->fld1 = res - 1;
o = o->next;
res = res % 16;
}
return res;
}
int main(int argc, char* argv[])
{
test_class o1;
test_class o2;
o1.fld1=1;
o2.fld1=1;
o1.next=&o2;
o2.next=&o1;
int n = 100*1000*1000;
int result=inner(&o1,n);
printf("%d %d\n",o1.fld5,result); // проверяем корректность и чтобы оптимизатор
// не выкинул неиспользуемый код
return 0;
}
// EOF
// запускаем:
g++ -O2 -o cpp-benchmark cpp-benchmark.cpp ; echo disassemble inner | gdb cpp-benchmark ; time ./cpp-benchmark
// листинг пропускаю.
real 0m0.225s
user 0m0.216s
sys 0m0.004s
Лисп на структурах:
;; struct-benchmark.lisp
(in-package :cl-user)
(list
'#.(restrict-compiler-policy 'safety)
'#.(restrict-compiler-policy 'debug))
(declaim
(optimize (speed 3) (safety 0) (debug 0)
(space 0) (compilation-speed 0)))
(defparameter *times* (* 100 1000 1000))
(defstruct test-struct
(next nil :type (or null test-struct))
(fld1 0 :type fixnum)
(fld2 0 :type fixnum)
(fld3 0 :type fixnum)
(fld4 0 :type fixnum)
(fld5 0 :type fixnum)
)
(declaim (inline test-struct-fld1 test-struct-fld2 test-struct-fld3 test-struct-fld4 test-struct-fld5 (setf test-struct-fld1) (setf test-struct-fld2) (setf test-struct-fld3) (setf test-struct-fld4) (setf test-struct-fld5)
test-struct-next))
(defun inner (o n)
(declare (type test-struct o))
(declare (type fixnum n))
(let ((res 0))
(declare (type fixnum res))
(dotimes (i n)
(incf res (the fixnum (test-struct-fld1 o)))
(setf (test-struct-fld2 o) (test-struct-fld1 o)
(test-struct-fld3 o) (test-struct-fld2 o)
(test-struct-fld4 o) (test-struct-fld3 o)
(test-struct-fld5 o) (test-struct-fld4 o)
(test-struct-fld1 o) (- res 1)
o (test-struct-next o)
res (mod res 16)))
res))
(defun main ()
(let* ((o1 (make-test-struct :fld1 1))
(o2 (make-test-struct :fld1 1 :next o1))
res)
(setf (test-struct-next o1) o2)
(setf res (inner o1 *times*))
(format t "~S~%~S~%" (test-struct-fld5 o1) res)))
(let ((*trace-output* *standard-output*))
(time (main)))
;;;; EOF
;; запускаем
>(load (compile-file "~/py/struct-benchmark.lisp"))
0.394 seconds of real time
0.436000 seconds of total run time (0.436000 user, 0.000000 system)
real time 3.879 seconds
;; clos-benchmark-with-types.lisp
(in-package :cl-user)
(list
'#.(restrict-compiler-policy 'safety)
'#.(restrict-compiler-policy 'debug))
(declaim
(optimize (speed 3) (safety 0) (debug 0)
(space 0) (compilation-speed 0)))
(defparameter *times* (* 100 1000 1000))
(defclass test-class-3 ()
((fld1 :initarg :fld1 :accessor test-class-3-fld1)
(fld2 :accessor test-class-3-fld2)
(fld3 :accessor test-class-3-fld3)
(fld4 :accessor test-class-3-fld4)
(fld5 :accessor test-class-3-fld5)
(next :initarg :next :accessor test-class-3-next)))
(defun inner (o n)
(declare (type fixnum n))
(declare (type test-class-3 o))
(let ((res 0))
(declare (type fixnum res))
(dotimes (i n)
(incf res (the fixnum (test-class-3-fld1 o)))
(setf (test-class-3-fld2 o) (the fixnum (test-class-3-fld1 o))
(test-class-3-fld3 o) (the fixnum (test-class-3-fld2 o))
(test-class-3-fld4 o) (the fixnum (test-class-3-fld3 o))
(test-class-3-fld5 o) (the fixnum (test-class-3-fld4 o))
(test-class-3-fld1 o) (the fixnum (- res 1))
o (test-class-3-next o)
res (mod res 16)))
(print res)
res))
(defun main()
(let* (
(o1 (make-instance 'test-class-3 :fld1 1))
(o2 (make-instance 'test-class-3 :fld1 1 :next o1))
res)
(setf (test-class-3-next o1) o2)
(setf res (inner o1 *times*))
(format t "~S~%~S~%" (test-class-3-fld5 o1) res)))
(let ((*trace-output* *standard-output*))
(time (main)))
#|
6.115секунд
|#
python:
# ~/py/oop-benchmark.py
import time
__times__ = 100*1000*1000
class TestClass3(object):
def __init__(self):
self.fld1 = 1
self.fld2 = 0
self.fld3 = 0
self.fld4 = 0
self.fld5 = 0
self.next = self
def inner(o,count):
res = 0
for i in xrange(count):
res += o.fld1
o.fld2 = o.fld1
o.fld3 = o.fld2
o.fld4 = o.fld3
o.fld5 = o.fld4
o.fld1 = res - 1
o = o.next;
res = res % 16
return res
def my_main():
o1 = TestClass3()
o2 = TestClass3()
o1.next = o2
o2.next = o1
res = inner(o1,__times__)
print '%s' % o1.fld5
print '%s' % res
my_main()
# запуск:
#time python oop-benchmark.py
#266666656
#3
#real 0m51.031s
#user 0m50.696s
#sys 0m0.052s
{oop-benchmark.fpc}
{$mode ObjFPC}
const n=100*1000*1000;
type
PTest = ^TTest;
TTest = object
public
fld1, fld2, fld3, fld4, fld5: Integer;
next: PTest;
end;
function inner(o: PTest; bound: Integer): Integer;
var i: Integer;
begin
Result:=0;
for i:=0 to bound-1 do with o^ do begin
Result:=Result+fld1;
fld2:=fld1;
fld3:=fld2;
fld4:=fld3;
fld5:=fld4;
fld1:=Result-1;
o:=next;
Result:=Result mod 16;
end;
end;
var
o1, o2: TTest;
b: Integer;
begin
o1.fld1:=1; o1.next:=@o2;
o2.fld1:=1; o2.next:=@o1;
b:=inner(@o1,n);
WriteLn(o1.fld5,' ',b);
end.
{
fpc -O3 oop-benchmark.fpc
Free Pascal Compiler version 2.6.4+dfsg-4 [2014/10/14] for x86_64
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling oop-benchmark.fpc
Linking oop-benchmark
/usr/bin/ld.bfd: warning: link.res contains output sections; did you forget -T?
36 lines compiled, 0.1 sec
1 warning(s) issued
den73@deb8:~/py$ time ./oop-benchmark
266666656 3
real 0m1.810s
user 0m1.776s
sys 0m0.008s
}
##########
# test.pyx file
import cython
cdef class TestClass:
cdef public unsigned int fld1, fld2, fld3, fld4, fld5
cdef public object next
def __cinit__(self):
self.fld1 = 1
self.fld2 = 0
self.fld3 = 0
self.fld4 = 0
self.fld5 = 0
self.next = self
@cython.overflowcheck(False)
@cython.cdivision(True)
cdef inner(TestClass o, count):
cdef unsigned int res = 0, i
for i in range(count):
res += o.fld1
o.fld2 = o.fld1
o.fld3 = o.fld2
o.fld4 = o.fld3
o.fld5 = o.fld4
o.fld1 = res - 1
o = o.next;
res = res % 16
return res
def main():
cdef TestClass o1, o2
o1 = TestClass()
o2 = TestClass()
o1.next = o2
o2.next = o1
res = inner(o1, 100_000_000)
print('%s' % o1.fld5)
print('%s' % res)
##########
# setup.py file
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
# to compile a module:
# python setup.py build_ext --inplace
extensions = [
Extension('test', ['test.pyx'],
extra_compile_args=['-O3'])
]
setup(name = 'access attrs benchmark',
ext_modules = cythonize(extensions, annotate=True),
)
#########
# main.py file
from test import *
main()
# Запуск с помощью pip
# 1. Создать ТРИ файла, исходник которых дан выше
# 2. В этой директории:
python setup.py build_ext --inplace
time python main.py
# Результаты:
real 0m0.306s
user 0m0.304s
sys 0m0.004s
$ time pypy oop-benchmark.py
266666656
3
real 0m0,761s
user 0m0,736s
sys 0m0,020s
EMACS Lisp - несколько быстрее Питона.
Сбылась мечта идиота - я почти сделал из лиспа дельфи :) Кое-что недопилено, конечно, но в целом результат вполне прощупывается:
опр функ Foo(SYM -- :л:CL.SYMBOL)
тело
л-печать(SYM.NAME())
зап SYM.VALUE = 6
л-печать(SYM.VALUE)
кно
Foo(цит(б))
(declaim (ftype (function (SYMBOL ) t) |Foo| ))
(proclaim '(ftype (function (SYMBOL ) t) |Foo| ))
(defun |Foo| (SYM)
(declare (type SYMBOL SYM))
(PRINT (symbol-name sym))
(setf (symbol-value sym) 6)
(PRINT (symbol-value sym)))
(|Foo| (QUOTE |б|))
Итак, что я наконец-то получил? Конечно, пример маленький и не во всём показателен, но:
1. Общее количество букв уменьшилось ВДВОЕ.
2. Количество скобок уменьшилось более чем ВЧЕТВЕРО.
3. Количество повторений слова symbol уменьшилось в ШЕСТЬ раз. Символ здесь, как вы понимаете, моделирует какой-то defstruct - а структуры я пока что в Яр не впилил.
4. Лисп, чувствительный к регистру символов, без вертикальный чёрточек.
5. Пошаговый отладчик. При трансляции можно включить инструментатор и можно будет действительно ходить по коду, а не прыгать в случайные места раз в сто километров, как это сейчас сделано в SBCL.
Вот, собственно, что я собирался сделать месяца за два летом 2015-го года. Надо было сразу перевести в следующую единицу измерения, и получилась бы правильная оценка трудоёмкости.
Что теперь я ожидаю от лисперов: я ожидаю, что они будут меня ругать. Написанный здесь код - это сумма множества предварительных этапов. Я публиковал свои решения на каждом этапе, и на каждом этапе меня ругали. Меня называли психом, блюб программистом и прочими другими плохими словами. Теперь настоящие лисперы должны просто лопнуть от злости. Хорошо лишь то, что их почти не осталось.
Давайте, начинайте называть меня психом и ламером, а я буду ржать над вами.
Да простит мне публика ламерский вопрос. Вот в С++ есть замыкания. Я немного почитал про них, но всё равно не уверен, что понял.
Я правильно понял, что closure object может присваиваться только копированием? И что если мы хотим, чтобы гигантские захваченные данные не копировались, мы должны решать это сами, захватывая такие ссылки объекты, которые реализуют нужное нам поведение с т.з. времени жизни? Просто я давно отстал от мира С++ и мне аж страшно на это всё смотреть.
Получить предпоследний элемент списка:
lindex {1 2 3 4} end-1
Питон:
some_list[-2]
Если сравниваем, то видим, что python и Перл - это кака, а tcl - конфетка. Проблемы питона: а вдруг я не имел в виду индекс с конца, а просто ошибся и неправильно вычислил индекс? А вдруг в списке всего один элемент? Правда, и tcl не выдаст ошибки, а выдаст пустую строку, но зато хотя бы есть способ _ясно_ сказать, что я имею в виду именно элемент энный с конца. Это вроде мелочь, но в tcl таких мелочей много и они хорошо складываются.
Соответственно, вопрос такой: откуда такие красивые мелочи в tcl, и где ещё подобное есть?
И вот предварительные итоги опроса
( читать дальше... )
Похоже, есть определённые движения в направлении добавления более качественной поддержки named-readtables в SLIME. На данный момент поддержка оформлена в виде довольно неэлегантного костыля, и вроде бы родилось решение, как начать переход к более правильному варианту без нарушения работы ныне здравствующего кода. Прошу происоединяться к дискуссии на SLIME-devel.
Хочу сделать автоматический layout своих нескольких toplevel окон, чтобы они разлеглись мозаикой. Если монитора более одного, то они должны разлечься на одном из мониторов.
Причём мозаика может быть неравного размера. Допустим, редактор занимает 3/4 экрана, а остальные окошки к нему сбоку прилеплены (как это обычно бывает в IDE)
(declaim (optimize (speed 3) (safety 0) (compilation-speed 0) (space 0) (debug 0)))
(declaim (ftype (function (fixnum ) fixnum) FOO ))
(defun FOO (X )
(declare (type FIXNUM X ))
(+ X 1)
)
(declaim (ftype (function (NUMBER ) NUMBER) BAR ))
(defun BAR (X)
(declare (type NUMBER X))
(FOO X)
)
Этот код компилируется без предупреждений. Если заменить number на string, то будет warning. Чтение мануала не помогло. Другие значения safety (до 3) не помогли.
| ← назад | следующие → |