LINUX.ORG.RU

Сообщения den73

 

extended file attributes для хранения кодировки

 extended file attributes,

Вот прочитал, что они существуют и в ext3 вроде как присутствуют.

Вопрос такой: можно ли в реальности использовать их конечному пользователю для хранения кодировки.

Например, в EMACS можно написать кодировку в первой строке. Но далеко не все редакторы это понимают, и нужно менять сам файл, если кодировка меняется.

А можно ли сделать так, чтобы кодировка хранилась в расширенных атрибутах файла, и чтобы я мог в следующих случаях сразу знать кодировку файла:

- файл открыт редактором (допустим, leafpad-ом) - файл получен по эл. почте в виде вложения - файл загружен через браузер - файл расшарен через самбу - файл перенесён из другой системы на флешке

Насколько я понял, и ext3, и ntfs в принципе позволяют хранить кодировку в extended attributes, вопрос лишь в том, чтобы всё это заработало как система.

Вот я и хочу понять, чего для работоспособности этой системы сейчас не хватает.

den73
()

Proof of concept для иммутабельности в SBCL

 , ,

Предыдущая серия фильма была здесь:

Исправить SBCL - добровольцы есть?

Теперь вот: http://lisper.ru/forum/thread/1361#comment-12370

https://github.com/budden/sbclt/commit/ba9ffd6d45441feed345b3a4dcfc1263daee94e9

Коротко говоря, ввёл в SBCL типы (mutable 1) (мутабельный) и (mutable 0) (иммутабельный). character, number иммутабельны всегда. Хеш-таблица иммутабельна, если она заморожена вызовом freeze-object. Если объект объявлен с типом (mutable 0), то (setf (gethash)) вызовет warning при компиляции, и в рантайме этот код упадёт.

Чего здесь сильно не хватает - это передачи выведенной информации об immutable в другое место. Например, пока нельзя объявить, что параметр у функции immutable, чтобы компилятор ругался при попытке передать в него mutable объект. На эту цель был ранее сделан заход, но полностью работающего решения не удалось получить. Видимо, займусь этим завтра и далее.

Также нужно, чтобы вывод типов понимал, что объект, который сейчас immutable, будет и впредь immutable, но про прошлое ничего сказать нельзя (т.е. в прошлом он мог оказаться и mutable). Я не настолько понимаю вывод типов в SBCL, чтобы это сделать. Нужно сидеть и разбираться - это нелегко.

И ещё - я пытаюсь фиксировать то, что узнаю по мере изучения SBCL. вот тут

den73
()

Портировать Яро-раскладку на Debian

 

https://budden73.livejournal.com/27328.html

Цена полного решения - 20 тыр.

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

den73
()

Исправить SBCL - добровольцы есть?

 ,

Я хочу следующие исправления/улучшения в 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*)
Это - баг в SBCL, который никого не интересует. Причина в том, что типы делятся на две категории: настоящие типы и предикаты. Принадлежность объекта к настоящему типу не меняется, например, к типу fixnum. Предикат (cons fixnum) может измениться.

Нужно внести в язык и в реализацию понятие о настоящем и ненастоящем типе и сделать так, чтобы вывод типов не использовал ненастоящие типы (или использовал их настоящую часть, в данном случае cons). При этом будет установлено, что defstruct-ы и deftype всегда будут переопределяться только вместе с unintern того символа, который их именует, за счёт этого настоящих типов станет гораздо больше.

Декларация const Нужно её как-то воплотить и использовать в системе типов. Соответственно, если объект const, то его можно передавать только в функцию, которая принимает const. Для структур, массивов и консов const распространяется только на первый уровнь ссылок (или рекурсивно? я не знаю, как надо сделать). Для классов CLOS - не нужно ничего делать, они меня не интересуют и я ими не пользуюсь.

Запрет неявного сужения типов Добавить новое качество оптимизации (к speed, debug и т.п.). Если это качество включено, то присваивание месту, декларированному как integer, типа number порождает warning или compilation error.

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

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

den73
()

golang: одноимённые методы в разных интерфейсах

 

Вопрос 1

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

Я пытаюсь написать видео плейер со звуком, и мне нужно реализовать в одной структре оба интерфейса. У каждого из них есть метод Извлечь() без параметров.

В C# это сделать можно, явно указав имя интерфейса, к-рый я реализую. В С++ при множественном наследовании (не ромбовидном) тоже можно (хотя бы в каких-то контекстах) указать, от какого предка я беру метод Извлечь. В Eiffel, если уж зашла о том речь, можно переименовать методы при наследовании. В CLOS родовые функции живут в разных пр-вах имён.

Мои действия в golang?

Вопрос 2 (он про Яр, конечно же)

Есть билиотека ::словари::, в ней есть тип контейнер, и у него метод контейнер%дай(строка). По соглашению, если ц - типа контейнер, то ц.дай превращается в контейнер%дай(ц). Т.е. примерно то же, что утиная интерфейсизация в golang, только интерфейс в данном случае состоит из одной функции. Я это придумал никак не глядя на голанг, а всего лишь пытаясь сделать костыль для неудобного обращения в лиспе к полям структур.

В библиотеке ::словари:: есть также generic код:

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

Но это - мина, которая позже рванёт.

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

И теперь мы делаем рефлексивный словарь. И оказываемся в тупике, т.к. нам нужно сделать две функции рефлексивный-словарь%дай с разным смыслом.

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

den73
()

кто понимает ковариантность?

 , , , , ковариантность

Просмотрел три статьи про вариантность, во всех неточности или ошибки:

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/

В контравариантности путаница (неправильно объяснено).

И т.д.

Вопрос: где же найти правильную статью про ковариантность, но без теорката (из моих трёх статей одна - с теоркатом, но её это не спасло от фактических ошибок). И заодно, предлагаю своё определение:

**********************

Вариантность - это возможность использовать подтип Пт вместо супертипа Ст или наоборот, в ряде частных случаев:

  • в место, типизированное как Ст, можно записать Пт
  • в место, типизированное как функция(Пт), можно записать функцию типа функция(Ст). В место, типизированное как функция() — Ст, можно записать функцию типа функция() — Пт
  • если типизированный контейнер Конт<T> только читается из места, типизированного как Конт<Ст>, то можно записать в него Конт<Пт> (в C# и в Java такая возможность замены требует особого указания в сигнатуре места). Если в него только пишется, то в место, типизированное как Конт<Пт>, можно записать Конт<Пт>
  • если виртуальная функция принимает Пт, то её можно перекрыть в потомке так, что она будет принимать Ст. Если ф-я возвращает Ст, то её можно перекрыть, чтобы она возвращала Пт.
  • если запись з1 отличается от записи з2 только тем, что поле з1.П1 - типа Пт, а з2.П1 - типа Ст, то з1 - подтип з2.

Т.е., получается, что отношение между типами зависит не только от самих типов или от их родственности (Конт<Т> могут быть друг другу не родственны или родственны странным образом через Object), но и от контекста применения

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

**********************

Прошу проверить правильность моего определения и сказать, все ли случаи применения вариантности в реальных ЯП я перечислил. Про Хаскель пока не спрашиваю, т.к. до функторов ещё не дочитал :) Но можете и про него написать :)

А вот предыдущая тема про это: Ковариантность и динамическая типизация

den73
()

Индексирование частично упорядоченного множества

 

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

Пока нашёл вот это: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.6760&rep=rep1... но даже не смотрел. Мне кажется, это должно быть в каком-то учебнике, который я не читал.

den73
()

Ищутся статически типизированные контейнеры для Common Lisp

 ,

Например, аналог Map<T1,Т2> с интеграцией в систему типов SBCL, чтобы можно было выкинуть проверки, когда тип известен во время компиляции?

den73
()

Ковариантность и динамическая типизация

 ,

Согласно принципу Лисков, экземпляр подтипа должен вести себя так же, как экземпляр супертипа. И вот у нас Keyboard - подтип Device, мы делаем какой-нибудь List<Device> (как бы супертип) и List<Keyboard> (как бы подтип).

Но на самом деле нет. Если мы рассмотрим полную сигнатуру функции «добавить» с учётом динамической типизации, то оказывается, что попытка вставить Mouse в List<Keyboard> имеет определённое поведение, отличающееся от такового для List<Device>. В первом случае будет ошибка времени выполнения (некорректный тип), а во втором мышь вставится в список.

Т.е. здесь нет ковариантности.

А теперь вопрос: что используется в ковариантности в языках, позволяющих скомпилировать код, вставляющий «нечто» (any,variant,t) в такой вот «псевдоковариантный» контейнер.

Мне кажется, должен быть какой-то костыль для этой ситуации, и я подозреваю, что он может присутствовать в Scala и/или в C#. Кто может что-нибудь сказать?

den73
()

Сравнение быстродействия виртуальных методов С++ и CLOS

 ,

Было бы интересно сравнить производительность вирт. функций.

Нужно учесть следующее: доступ к полям в виде родовых функций намного-намного медленнее доступа к полям структуры, за исключением частных случаев (например, в теле метода, специфицированного этим классом, может быть лишь раза в полтора медленнее структуры, согласно рук-ву 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
6.7 секунды

С++

// 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.54 секунды.

Ну и для полноты картины, создаваемая объектная система для Яра:

  • Полностью оптимизирована под отладку, смещение в VMT выражено глобальной переменной, вызов функции через символ - 31 секунда.
  • то же, но вызов функции как функции 16 секунд
  • то же, оптимизация под скорость - 4.2 секунды

Получилось в 1.6 раза быстрее CLOS и в 2.7 раз медленнее С++. Правда, для множественной диспетчеризации скорее всего проиграем против CLOS. С другой стороны, сделать inline статический диспатч для CLOS методов в случае, когда тип достаточно хорошо известен, мне не удалось, оно делается с помощью MOP. Я делаю примерно то же, но без CLOS.

den73
()

кортежи не нужны?

 

Вот мне всегда казалось, что кортежи - это лишнее понятие. Сейчас копаюсь в потрохах SBCL и вижу, насколько они усложняют вывод типов и систему типов. Скажем так, более чем вдвое. В Си можно можно просто определить struct. Можно его инициализировать двумя значениями в красивой форме

struct gasket {
    float weight;
    unsigned height;
    unsigned diameter;
};
 
void main() {
    struct gasket obj = { 12.f, 120, 30 };

Можно вернуть структуру из функции, можно передать в функцию. Спрашивается, а зачем тогда кортежи? Что нового они вообще дают хоть в каком-то языке? Понятно, что в языках, где список может состоять из элементов только одного типа, и где нет типа any/t/variant, кортежи могут быть кривым костылём для полиморфных списков. Ну и теоретически они могут быть эффективнее. Но Си всё равно даёт то же самое, плюс поля кортежа ещё и именованные. Единственное неудобство - это то, что тип структуры нужно объявлять. Но в этом можно видеть плюс, т.к. объявление структуры заставляет дать вещам имена и тем самым сделать программу более явной и понятной. А типизация полей структуры убережёт от ошибок.

Единственное, что я заметил - так это то, что времена жизни, константность и прочие атрибуты объектов в кортеже никак не связаны, а в структуре - связаны. Но если структура наполнена указателями, то и это почти не имеет значения.

Что я упустил?

den73
()

Как в Хаскеле сделать try..catch..finally?

 ,

Как написать вот это:

зайти-в-сарай()
try
  ткнуть-палкой-в-осиное-гнездо-в-сарае()
catch all
  вернуть "Ой"
end

и вот это:

зайти-в-сарай()
try
  ткнуть-палкой-в-осиное-гнездо-в-сарае()
finally
  уходя-из-сарая-погасить-свет()
end

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

den73
()

За какую зарплату вы согласились бы фулл-тайм программировать на Common Lisp?

 ,

Это не предложение о работе. Это просто вопрос. Прежде всего, вопрос обращён к тем, кто знает лисп, а не к школьникам, которые просто хотят много зарабатывать.

Перемещено jollheef из job

den73
()

Транслятор подмножества Common Lisp в GLSL

 ,

https://github.com/cbaggers/varjo

1500 коммитов за 4 года, два контрибюьтора.

  • Поддерживает макросы, в т.ч. define-compiler-macro
  • Статическая типизация
  • multiple-values
  • defstruct
  • ограниченная поддержка ф-й первого класса (функции должны быть разрешены статически)
  • inline GLSL expressions
  • WIP for declare and extensible declaration identifiers
  • проверка типов внутри и между этапами шейдинга

Остальное не понял.

> (ql:quickload :varjo)

den73
()

относительно живой проект по улучшению документации Common Lisp

 

den73
()

Сравнение производительности доступа к полям структур в Python, Common Lisp и С++

 , cоmmon lisp, , ,

Помните, я сказал, что постараюсь не писать до конца сентября? Так вот: у меня не получилось :) Кому не нравится тут тег Яр - заверяю, что он не для рекламы (и так все уже знают), а исключительно для удобства: мне по нему удобно искать, а вам его удобно заигнорить. Отношение к Яру здесь такое: я мучаюсь, как мне реализовать объекты Яра. Склоняюсь к тому, что сделать их с опцией «реализовать как 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)
Лисп, но вместо inline ставим notinline - все аксессоры превращаются в полноценные функции. Получаем
real time 3.879 seconds
Лисп на CLOS:
;; 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
FreePascal
{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
}

cython, pypy

##########
# 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 - несколько быстрее Питона.

den73
()

ура, наконец-то в Яре достигнут какой-то успех

 ,

Сбылась мечта идиота - я почти сделал из лиспа дельфи :) Кое-что недопилено, конечно, но в целом результат вполне прощупывается:

опр функ 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-го года. Надо было сразу перевести в следующую единицу измерения, и получилась бы правильная оценка трудоёмкости.

Что теперь я ожидаю от лисперов: я ожидаю, что они будут меня ругать. Написанный здесь код - это сумма множества предварительных этапов. Я публиковал свои решения на каждом этапе, и на каждом этапе меня ругали. Меня называли психом, блюб программистом и прочими другими плохими словами. Теперь настоящие лисперы должны просто лопнуть от злости. Хорошо лишь то, что их почти не осталось.

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

den73
()

Замыкания, С++ и память

 , ,

Да простит мне публика ламерский вопрос. Вот в С++ есть замыкания. Я немного почитал про них, но всё равно не уверен, что понял.

Вот например

Я правильно понял, что closure object может присваиваться только копированием? И что если мы хотим, чтобы гигантские захваченные данные не копировались, мы должны решать это сами, захватывая такие ссылки объекты, которые реализуют нужное нам поведение с т.з. времени жизни? Просто я давно отстал от мира С++ и мне аж страшно на это всё смотреть.

den73
()

Где можно обращаться к предпоследнему элементу списка как List(END-1) ?

 , ,

Получить предпоследний элемент списка:

lindex {1 2 3 4} end-1

Питон:

some_list[-2]
В Перле подобное же.

Если сравниваем, то видим, что python и Перл - это кака, а tcl - конфетка. Проблемы питона: а вдруг я не имел в виду индекс с конца, а просто ошибся и неправильно вычислил индекс? А вдруг в списке всего один элемент? Правда, и tcl не выдаст ошибки, а выдаст пустую строку, но зато хотя бы есть способ _ясно_ сказать, что я имею в виду именно элемент энный с конца. Это вроде мелочь, но в tcl таких мелочей много и они хорошо складываются.

Соответственно, вопрос такой: откуда такие красивые мелочи в tcl, и где ещё подобное есть?

И вот предварительные итоги опроса

( читать дальше... )

den73
()

улучшение поддержки named-readtables в SLIME

 , ,

Похоже, есть определённые движения в направлении добавления более качественной поддержки named-readtables в SLIME. На данный момент поддержка оформлена в виде довольно неэлегантного костыля, и вроде бы родилось решение, как начать переход к более правильному варианту без нарушения работы ныне здравствующего кода. Прошу происоединяться к дискуссии на SLIME-devel.

den73
()