?

Log in

No account? Create an account

ตุ๊กแก

Вот такой крокодил живет у нас на чердаке. По вечерам гуляет по стенам и окнам снаружи, охотится, громко кукует.


Read more...Collapse )

Tags:

Больше белки

Видео с телефона:



в шапке

Мой младший член семьи спит в шапке. Залезает в нее с ногами и хвостом, и спит в ней.

У нас во дворе живет собака, и как-то в конце февраля она нашла что-то в траве в трех метрах от нашего дома, раздался писк, будто от резиновой игрушки. Жена заподозрила, что это может быть не игрушка, пошла проверить. Собака держит находку в пасти и показывать отказывается. Зная собакину страсть к обуви, жена предложила ей взамен босоножку. Собака тогда открыла рот, и оттуда выпал свернутый комком зверь, которого мы тут же подобрали. Оказалось - бельчонок. Примерно недельного возраста, меха еще совсем мало, глаза еще не открылись. Но целый, повреждений не видно, разве что одно колено чуть опухло.

photoCollapse )
Стали его дома растить. В англоязычном интернете в таких случаях рекомендуют обращаться в ближайший центр реабилитации белок. Позавидовали тем странам, где такое бывает. От нас до ближайшего часов десять на самолете лететь, небось. Но узнали, что надо его во-первых хорошо согревать, в таком возрасте они сами еще плохо с терморегуляцией справляются, а во-вторых кормить его стоит детским питанием ("формулой") для котят. Коровье молоко или там человечье детское питание им не годятся, от них белки мрут. Знакомая ветеринарша подкинула контакты местного специалиста, который уже много белок вырастил, пообщались с ним, да еще с другой ветеринаршей, у которой тоже белка росла. Тут это не редкость, бельчонка найти. Благодаря их советам, бельчонок у нас выжил и чувствует себя весьма неплохо. Первые несколько недель он жил с закрытыми глазами, почти не двигаясь. Утром возьмешь в руки, чувствуешь, что лапы у него холодные, мерзнет. Придумали рядом с его коробкой на ночь оставлять чайник с водой в режиме подогрева, этого небольшого источника допольнительного тепла хватило, мерзнуть перестал. Кормить его надо бы каждые 3-4 часа. Жена как к младенцу вставала по ночам, кормила. Если ночью бельчонок долго не ел, утром во время еды могут быть судороги из-за нехватки глюкозы. Исправляется небольшим количеством меда внутрибелочно. Мед очень любит!
Теперь ему уже ~два месяца, глаза открыл, подрос, покрылся густой шерсткой, мерзнуть перестал, ночью тоже сам справляется, можно не вставать. Бегает по дому, лазит по дивану, шторам и карнизам. Очень любит круги наматывать по джинсам на мне, как на дереве. Начинает есть фрукты. Очень любит яблоки, также ест гуаву и манго. Всякие орешки пока игнорирует, за еду не считает. Людей не боится, сам бежит в руки. Очень любит мыться в теплой воде, в ней млеет и расслабляется. В связи с этим норовит теперь в чашку с чаем залезть, приходится или рукой кружку закрывать, или на время чаепития закрывать бельчонка в комнате, за что он потом на нас ругается, цокает. Может во время еды забраться по штанам и по спине на руку и залезть прямо на вилку. Ночью спит теперь в дупле - корзинке, подвешенной у карниза. Днем может спать на карнизе и, поскольку тот скользкий, шлепаться оттуда вниз, когда крепко заснет. Очень славный зверь, такого дома держать прям не хуже кота, даже интересней. Со временем планируем выпустить на волю, когда подрастет и лучше с местными фруктами освоится.

more photosCollapse )

задачка с типами

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

foobar :: Int -> String -> String -> String
foobar a b c = if a > 0 then b++c else c++b

fun1 :: Double -> Int -> String
fun2 :: String -> Int
fun3 :: Int -> Double -> Double -> Double
и т.д.

Есть "динамический" тип - сумма вышеназванных, простой алгебраик

data Var = Integer Int | Number Double | Str String

и нужно реализовать такую функцию callFun, которая получает список этих вот Var'ов и одну из ф-й выше, и вызывает переданную функцию с переданными аргументами, распаковав их из списка алгебраиков в конкретные типы. Если, конечно, переданный список значений подходит по типам и количеству к вызываемой ф-ии. Ну а не подходит - так выдать ошибку. Чтобы можно было вызвать ее навроде

callFun foobar [Integer (-3), Str "bb", Str "cc"]

и вернуть ее результат. По сути это такой FFI для интерпретатора.
Как бы вы такое сделали на вашем любимом статически типизированном ЯП?

Вот как решение выглядит на D. Сначала покажу результат, как оно выглядит в использовании:

union MyTypes {
    int integer;
    double number;
    string str;
}

alias Var = Sum!MyTypes;

string foobar(int a, string b, string c) {
    return a > 0 ? b~c : c~b;
}

double nsqrt(double x, int n) {
    import std.math : pow;
    return pow(x, 1.0/n);
}

void main() {
    Var a = -3;
    Var b = "bbb", c = "ccc";
    callFun!foobar([a,b,c]).writeln; // выводит cccbbb

    [Var(256.0), Var(4)].callFun!nsqrt.writeln; // выводит 4
}


По мотивам библиотечки taggedalgebraic, описываем типы для суммы в виде union'a. Конструктор типов Sum берет такой юнион и делает из него discriminated union, структуру, где кроме исходного union'a еще тэг, дискриминатор, сделанный типом-перечислением, причем имена тэгов берутся из имен полей данного юниона.

struct Sum(U) {
    alias Names = AliasSeq!(__traits(allMembers, U));
    mixin(`enum Tag {` ~ [Names].join(", ") ~ `}`);
    U data;
    Tag tag;

    this(T)(T x) {
        enum name = Names[staticIndexOf!(T, Fields!U)];
        __traits(getMember, data, name) = x;
        tag = __traits(getMember, Tag, name);
    }
}


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

auto use(alias fun, A)(A a) {
    final switch(a.tag)
        foreach(name; A.Names)
            case __traits(getMember, A.Tag, name):
                return fun(__traits(getMember, a.data, name));
}

Тут мы делаем switch по тэгу, а все возможные варианты тэгов у нас перечисляет foreach по компайл-тайм списку их имен, хранящемуся в переданном алгебраике А. Такой цикл по компайл-тайм списку разворачивается при компиляции, так что на каждой итерации получается своя ветка свича, в разных ветках идет обращение к разным полям юниона, значение извлекается по имени через __traits(getMember), при этом на разных итерациях цикла (ветках свича) у нас разный тип этого значения, и оно передается в хэндлер fun.
Теперь уже можно реализовать callFun:

auto callFun(alias fun)(Var[] args) {
    alias PS = Parameters!fun;
    assert(PS.length == args.length, "Error: expecting " ~ PS.length.text ~ " arguments.");
    Tuple!PS params;
    foreach(i, T; PS)
        args[i].use!((x) {
            static if (is(typeof(x)==T)) params[i] = x;
            else throw new Exception("type error: passing " ~ typeof(x).stringof
                                ~ " instead of " ~ T.stringof);
        });
    return fun(params.expand);
}


callFun получает на вход какую-то ф-ю fun (известна в момент компиляции) и массив из Var'ов.
В первой строчке получаем компайл-тайм список PS типов агрументов переданной функции.
Если по длине не совпадает с переданным набором значений - бросаем ошибку.
Заводим тупл params, содержимое которого по типам совпадает с типами аргументов переданной ф-ии. Т.е. если fun принимает string и int, то params будет иметь тип Tuple!(string, int).
Проходимся разворачивающимся в компайл-тайме циклом по этому списку типов и заодно по массиву переданных Var'ов:
для очередного переданного значения args[i] типа Var (наш алгебраик), передаем его в use вместе с лямбдой, которая получит содержащееся внутри args[i] значение уже конкретного типа. Внутри use будет скомпилирована попытка применения лямбды ко всем возможным типам, которые могут хранится в том алгебраике. Поэтому внутри лямбды мы смотрим на тип аргумента - typeof(x) - и если он совпадает с нужным типом из списка типов аргументов fun, то значит это значение x может быть записано в тупл params. Например, если PS это (string, int), и в массиве Var'ов args элемент с индекcом 1 содержит внутри целое число, то use, сделав switch по тэгу, перейдет в ветку про int и передаст целочисленное значение из юниона в лямбду. Когда оно будет передано в эту лямбду, мы сможем его записать в params[1]. Если же в args[i] было передано значение не того типа, сработотает другая ветка свича по тэгу, то когда то значение попадет в эту лямбду, static if выведет на альтернативную ветку, где мы бросим исключение - переданное значение по типу не подошло.
Ну вот, успешно заполнив таким образом тупл params, где уже сидят значения конкретных типов, осталось вызвать требуемую ф-ию с этими значениями, что и делается в последней строчке. Вот и все!
Несколько строчек, и получился универсальный код, который априори вообще не знает, с какими алгебраиками, какими наборами типов ему придется работать, и работает с любыми. При этом тут нет никакой рантайм рефлексии помимо одного switch'a по тэгу, что мы написали явно. Все проверки вроде typeof(x)==T делаются при компиляции, в рантайме их нет, все имеющиеся циклы тоже разворачиваются в компайл-тайме. В требуемую функцию передаются только правильные типы, это проверяется статически. Но вот что интересно, некоторые части программы не подпадают ни под обычное определение статической типизации, ни под динамической. Тип переменной x в callFun нельзя так просто рядом написать карандашом, как это предполагает статическая типизация. Но и динамическим в обычном смысле он не является. Вот если все вызовы callFun с разными ф-ями и все циклы внутри развернуть, получится уже вполне код уровня привычных статически типизированных языков, где каждой переменной можно статически сопоставить какой-то тип. Такое ощущение, что шаблонов/макросов в типизированных языках не хватает какого-то своего раздела в теории типов / PLT. Когда один терм в исходнике у нас обозначает несколько разных термов после раскрытия шаблона, и у них разные типы.

Tags:

Избирательный участок

В нашем часовом поясе выборы уже начались. Оказалось, что участок открылся даже на нашем острове. Из любопытства съездили посмотреть.



немножко фотокCollapse )

Ехать на выборы тут надо через джунгли да через горы. Дорога между недавно открывшимся почетным консульством и моим домом:

Щастье

На днях узнал, что оказывается Far был портирован на линукс, можно взять на гитхабе. Поставил, работает, ура! А то у меня руки очень к нему привыкли, mc хоть в целом и похож, но о различия все время спотыкался, на уровне мышечной памяти. А тут прям опять как дома.

Скевоморфизм

В коммерческом фантастическом кино и играх картинка должна быть а) зрелищной, но б) достаточно понятной, а значит привычной для обывателя. Поэтому в космосе мы будем слышать выстрелы и взрывы, лазеры будут стрелять яркими полосками, летящими не быстрее пули, иноплянетяне почти поголовно будут человекоподобными - того же размера и схожего строения, будут говорить ртом и понимать человеческую речь, прически у героев будут как это было модно в момент выхода фильма, межпланетная связь будет мгновенной, часы будут идти синхронно, ну а космические корабли будут похожи на самолеты - в частности, с двигателями сзади, которые работают постоянно, даже если корабль подлетает к пункту назначения, и даже если он тормозит. И чтобы сюжет был эмоциональным, герои будут порой вести себя иррационально.
Это законы жанра. Нарушишь - потеряешь в аудитории и деньгах. Кому это не очевидно?
Но, похоже, среди аутичных "технических специалистов" постоянно находятся те, кого это смущает, кто ищет реализма и рациональности в фантастике. А у меня с каждым разом все никак не получается не посчитать их идиотами. Постараюсь исправиться.

ну всё теперь



(сам внезапно набирающий популярность ответ здесь)

Tags:

LDC

Сегодня узнал, что в андроидном Termux'e можно сказать "apt install ldc" и можно прям на телефоне писать на D:

До этого у меня там уже стояли gcc, python и ruby. Там же есть vim, он уже знает про все эти языки. Пока что единственное применение было - написал солвер для одной игрушки.

Tags:

конец хаскеля

Свежее выступление Майкла Сноймана:
https://www.youtube.com/watch?v=KZIN9f9rI34
"Вот посмотрим на простейшие монадные трансформеры. Тут фигня, и тут фигня. Тут не срастается. Тут не работает. Тут данные теряются. Тут опять фигня. Мой совет: не вы#$%йтесь, используйте мутабельные переменные, кидайте рантайм исключения."

Tags: