?

Log in

No account? Create an account
nyaload

Журнал Пушыстого

Журнал Пушыстого

Entries by category: it

Все течет, все меняется
nyaload
_winnie
Тонкая ошибка в такой схеме базы данных про книги и их авторов:


таблица 1: id книжки (ключ) | список id авторов книги | название книжки

таблица 2: id автора (ключ) | имя автора

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

--------------------------------------------------------------
Никогда-никогда-никогда-никогда не буду больше использовать доставку EMS россии. Единственное что там лучше чем Почта России - это хорошая поддержка по телефону.

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

--------------------------------------------------------------

А ещё я почти научился фотореалистичным срисовкам (на фото сверху IRL натюрморт, на экране снизу - срисованное):
Read more...Collapse )

Как закрыть панель в Blender
nyaload
_winnie
Blender - наиболее популярный свободный и кроссплатформенный редактор для создания 3d-анимации. В добавление к созданию анимации - там есть поддержка монтажа нарендеренного. Сегодня немножко потыкался в его UI. Как ни странно, это единственная программа которую я нашёл, в которой можно редактировать видео для простого монтажа в разных форматах. В других - то .wmv не загружается, то .mov не загружается (и похоже нет ни одной программы, которая могла бы резать видео по key-frames без перекодировки в разных форматах-контейнерах, включая mov, wmv, avi, mkv).

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

Если вы не осознали, что тут выше написано - то ничего страшного, я тоже не понял, пока я не посмотрел ролик на ютубе.

На самом деле при закрытии панели - нужно указать не только закрываемую панель, но и то, какую другую панель использовать для оставшейся дырки. Универсальность и точность!


Вопрос на stackexchange, как закрывать панели в blender - http://blender.stackexchange.com/questions/1223/how-to-close-open-a-view-panel
Ролик на ютубе, объясняющий как закрывать панели в blender - https://www.youtube.com/watch?v=AMBi1R7KB48
Человек полгода работающий в blender, спрашивает как закрывать панели - http://blenderartists.org/forum/showthread.php?234374-Closing-Split-Screens

Говорят, что это более удобно, чем то что было в более ранних версиях, <2.6

Видно, не один я был в тупике, как же это делается:


Это не единственная непривычная вещь в интерфейсе. Ещё есть кастомный Open/Save File диалог, в котором нельзя открыть файл по его абсолютному пути (два отдельных текстовых поля для директории и имени файла). И кастомная логика сохранения проекта. Благодаря которой я несколько раз закрыл проект, не сохранив - дефолтная кнопка при закрытии это "да, выйти не сохранив", вместо привычного "выйти и сохранить". Если закрыть по 20-летней привычке Open/Save File диалог крестиком - то закроется текущий проект, а не этот диалог. Если не прочитать внимательно модальный диалог при закрытии Open/Save - то выход будет без сохранения. Это то, об что я стукнулся лбом при освоении самой базовой функциональности и UI блендера.

Видимо, разработчики Блендера совсем не наблюдали, какие проблемы возникают у начинающих, и не считают благим делом сокращение времени обучения.

Иммутабельность мешает иммутабельности
nyaload
_winnie
Всё-таки мутабельность объектов в небольших пределах - иногда весьма облегчает написание алгоритмов. Это здорово, когда можно в небольшом обозримом участке кода создать объект A, перебросить на него указатели из другого уже созданного объекта B (который мы только что создали), и отложить заполнение полей A до того момента, когда они станут известны. Но в иммутабельном языке - невозможно создать объект, пока мы не знаем все параметры для его создания, и это дико мешает при переложении известных алгоритмов на такой язык. Нужно вводить дополнительные структуры данных, чтобы поменять порядок создания объектов A и B, так как мы в случае иммутабельности не можем создать A, а потом провести из него указатель в B.

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

Это, например, мешает переписывать из википедии алгоритм персистентного красно-черного дерева на такой язык. Там есть подзадача, похожая на такую: в односвязном списке поменять значение ноды с удаляемым значением где-то внутри списка на значение из ноды в конце списка. Список персистентный, поэтому "поменять" означает "создать клон с нужным изменением".
В языке, где можно менять значения полей объекта - всё тривиально, решается в один проход от начала до конца списка.
Идем от начала до нужной внутренней ноды списка, пока идём - создаем дубликаты проходимых нод. Когда нашли нужную ноду - запоминаем указатель на её дубликат, доходим до конца списка, копируем значение в созданный дубликат. Лишней памяти, при условии что нам надо сохранить и старый список (персистентность в copy-on-write её варианте) - не тратится.


В случае же иммутабельного языка программирования - нельзя начать создавать новый список, пока мы полностью не разобрались, какое значение положить в его середине.
Так как пока не найдена нужная нода с изменяемым значением - мы не можем создать все ноды перед перед ней, включая первую. А ноду с изменяемым значением мы не можем создать, так как не знаем новое значение из конца списка.
Получается, что мы тратим дополнительные мозги на изобретение того, как же перевернуть задом наперед заполнение списка, и тратим на это память (иногда неявно, на параметры глубокой рекурсии).

π++
nyaload
_winnie
в С++ нет числа π в стандартной библиотеке. M_PI/_USE_MATH_DEFINES зачем-то сломали в g++ при использовании -std=c++11, а взамен ничего не предложили.

http://stackoverflow.com/questions/1727881/how-to-use-the-pi-constant-in-c

В результате проще в каждом cpp файле копипастить строчку «const double pi = 3.14159265358979323846;» (или «constexpr auto pi = 3.14159265358979323846» в c++11, или acos(-1) вместо константы).

--------------
PS. этот пост дубликат предыдущего, предыдущий я удалил из-за этого бага. Уже оставленные чужие коментарии достал из почты, скопирую в комментарии.
Tags:

shared_ptr
nyaload
_winnie
У std::(boost::)shared_ptr в блоке, который хранит счетчик, есть ещё указатель на функцию удаления (deleter), который запоминается в момент создания shared_ptr
Это позволяет:
* не париться с виртуальным деструктором, если мы кастуем shared_ptr<derived> в shared_ptr<base> (not recommended, если shared_ptr это не единственный указатель в проекте).
* не париться с переносом деструктора из .h в .cpp, когда делаем PImpl
* передавать shared_ptr в другой модуль, у которого свой собственный аллокатор памяти и свой delete
* Вообще не привязывать способ удаления к типу в shared_ptr (можно использовать свой кастомный аллокатор, и пользователям shared_ptr<T> не нужно знать, какой аллокатор использовался при создании T).

Так же у shared_ptr есть отдельный счетчик для weak-ссылок (иногда нужно в многопоточном программировании и прочих случаях "мы не можем статически отследить вложенность lifetime").

Функция make_shared позволяет не делать две аллокации на счетчик и объект, объединяя их в один кусок памяти.
Tags:

binary search
nyaload
_winnie
В книге "жемчужины программирования" сообщается, что невозможно написать binary search с первого раза.

На самом деле его невозможно написать с первого раза, если не осознаешь что такое инвариант цикла. Если осознаёшь - то скорее всего запросто напишешь (я вот уже в третий раз за год написал).

Если не осознаёшь инвариант цикла, то с большой вероятностью будет одна из ошибок:

* mid = (lo+hi)/2 или hi/2+lo/2 ? (и нет, дело не в overflow, эти формулы просто разные (1+3)/2 != 1/2 + 3/2 ).Read more...Collapse )

Как писать двоичный поиск чтобы он не бажил:
* Выбрать инвариант "если искомый элемент существует, то он в [lo, hi)", изначально выбрать lo,hi = 0, N.
* mid = lo+(hi-lo)/2 (при этом для кода верно утверждение "промежуток длиннее 1 всегда укоротится").
* Если ищем первый среди равных - то сравнение выглядит как key<=array[mid-1], если последний - key<array[mid]
* в зависимости от сравнения присваиваем mid в lo или hi (в любом случае, не mid-1).



Двоичный поиск для равномерно распределённых ключей (хешей) ускоряется простым хаком "для каждого из 65536 двубайтовых префиксов запомнить, где он начинается", это убирает 16 начальных итераций.Read more...Collapse )
Tags:

В один мешок помещается K яблок. Сколько мешков надо, если яблок - N штук.
nyaload
_winnie
Часто встречающаяся в программировании (тривиальная) задача:
В один мешок помещается K яблок. Сколько мешков надо, если яблок - N штук.
Нет, не N/K. И даже не (N/K)+1. На самом деле, (N+K-1)/K.

Убираем одно яблоко (т.е. остаётся N-1), если какой-то мешок заполнен не полностью, то убираем именно из него. Смотрим сколько полностью заполненных мешков нужно для такого количества. Это (N-1)/K. Добавляем обратно тот мешок, из которого вынули яблоко, получаем на 1 больше, т.е. 1 + (N-1)/K.
Почему именно одно яблоко? Потому что похожее рассуждение "уберем 0 яблок" не работает для N=42*K, а "уберём 2 яблока" не работает для N=42*K+1. Требуется чтобы мешок из которого убрали яблоки - был ровно один (для двух яблок это не так), и чтобы он перестал быть полным (для 0 яблок это не так).

Можно ещё рассуждать так: количество мешков увеличивается монотонно, и ровно на 1 при увеличении N на K. Отсюда визуальный способ мышления подсказывает, что график формулы - это "лесенка", где длина ступеньки равна K. Такая лесенка задаётся формулой N/K. Осталось только понять, как сместить вверх и вбок такой график для совпадения с искомой формулой. Значит формула должна быть вида константа1 + (N+константа2)/K, осталось подобрать константы. Разрыв в количестве мешков - в тот момент, когда яблок на одно больше, чем для полного заполнения всех мешков (т.е. N делится на K). Отсюда константа2=-1. константа1 подбирается на любом частном случае.

Чтобы формула корректно работала для N=0 и не делить отрицательное (N-1) - надо переместить слагаемое-единицу в знаменатель дроби, т.е. формула становится (N+K-1)/K.
В C/C++ отрицательные числа делятся не так, как в Python, в Python деление -1 на K сработает корректно и можно использовать 1+(N-1)/K, в C/C++ - нет, и лучше использовать (N+K-1)/K
Если K - степень двойки, то такой проблемы нет, битовые знаковые сдвиги отработают корректно в 1+((N-1)>>10).
Если N настолько большое, что есть риск IntegerOverflow - то N/K + bool(N%K)

Вроде тривиально, но как часто люди до этого не додумываются и лепят или каких-то монстров, или double-арифметику, или тратят один лишний мешок.

Особенности реализации #pragma once в gcc
nyaload
_winnie
Это исследование с gcc написал Vladimir Kozbin ( https://twitter.com/wawakadesu ), я его сюда только лишь скопировал.
--------------------------------------------------
Задачка.

Возьмём файлик a.h

#pragma once
x += 1;

Read more...Collapse )
Tags:

Атака на интерактивную сессию
nyaload
_winnie
Подумал, что в случае интерактивнной работы человека по защищённому каналу - легко определить длину команд или вводимых паролей, прикинуть какой софт используется (если альтернатив немного), используется ли мышка в x-сервере, сколько пикселей она пробегает. Или даже какие кнопки на клавиатуре нажимаются, если мы знаем что человек вводит "12" быстрее, чем "95"

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

upd: только подумал, как гугл уже предложил "ssh keystroke timing attack".

javascript spoiler
nyaload
_winnie
Тег <lj-spoiler> </lj-spoiler> предназначен для того, чтобы [не подглядывать!]случайно не показать читателю то, что он не хочет видеть (например, подсказка к решению головоломки или сюжет книги).

Но он не работает при отключенном JavaScript.

Можно использовать, чтобы показывать неприличные слова только тем, у кого включён JS.
Tags: