Разворот односвязного списка за O(n) (память — O(1)) на Гугл Гоу
Вчера друг подвозил меня до дома, я решил, пока в пробке, попрограмировать на «Гоу». Практики давненько не было, язык уже начинает забываться, не модули, это ерунда, а сами конструкции, вот что неприятно.
Взял задачу — развернуть односвязный список за O(n) с константным ограничением на память. Задачу частенько дают на собеседованиях, как я слышал, говорят мало кто решает, решил посмотреть действительно ли это сложно. В уме прикинул, подумалось что нет, практика показала, что я не ошибся. В общем, выкладываю что получилось.
package main
import (
"os"
"bufio"
)
// тип для односвязного списка
type links struct {
Next *links
Value string
}
// вывод на печать односвязаного списка
func (chain *links) Print() {
for chain != nil {
os.Stdout.WriteString(chain.Value)
chain = chain.Next
}
}
// разворачиваем односвязный список за O(n)
func (chain *links) Reverse() *links {
if chain == nil || chain.Next == nil {
return chain
}
left, next := chain, chain.Next.Next
chain = chain.Next
left.Next = nil
for {
chain.Next = left
if next == nil {
break
}
left, chain, next = chain, next, next.Next
}
return chain
}
// Создаём список из чего-нибудь, умеющего выдавать по одной строке за раз
func CreateFromReader(readline func() *string) *links {
var chain, start *links = nil, nil
for {
if line := readline(); line != nil {
link := &links{ nil, *line }
if chain == nil {
chain, start = link, link
} else {
chain.Next, chain = link, link
}
} else {
return start
}
}
return nil
}
// Точка входа
func main() {
if len(os.Args) < 2 {
os.Exit(1)
}
f, err := os.OpenFile(os.Args[1], os.O_RDONLY, 0666)
if err != nil {
os.Exit(2)
}
defer f.Close()
r := bufio.NewReader(f)
CreateFromReader(
func() (*string) {
if line, e := r.ReadString('\n'); e == nil {
return &line
}
return nil
},
).Reverse().Print()
}Список создаётся из файла, который нужно передать программе первым параметром. Вообще, вся реализация разворота — в методе Reverse класса links, остальное — заполнение структуры и вывод на печать. Оцените как мало надо кода, чтобы решить это задание.
Angry Spy
Требуются: встречи, контакты, удостоверение телефона, удостоверение владельца, съёмка фото и видео, службы определения местоположения, воспроизведение мультимедия, микрофон, службы данных, служба
push-уведомлений, датчик движения и направления
Зачем это всё (разве что кроме «службы
Возраст объектива Кэнона
Возраст зашифрован в первых двух цифрах десятизначного серийного номера, который должен выглядеть так: ДД П ССССССС, где «ДД» — код даты, «П» — скорее всего номер партии, а «ССССССС» — уникальный номер объектива.
Вбейте в строку адреса браузера следующий код:
javascript:isNaN(n=prompt('Введите первые две цифры номера вашего объектива:')-61)||alert((n+=(n<6)*100)%2512+'/'+~~(2010+n/12))нажмите «Enter», введите в появившееся окно первые две цифры с объектива, снова нажмите «Enter», получите дату изготовления.
Из документации по «Сфинксу»…
Из документации по поисковому движку «Сфинкс» (смешную фразу я выделил):
If this requirement is not met, different bad things can happen. For instance, Sphinx can crash with an internal assertion while indexing; or produce strange results when searching due to conflicting IDs. Also,
a 1000-pound gorilla might eventually come out of your display and start throwing barrels at you. You’ve been warned.
Потрясающая история деревянной мыши
На «Хабре» статья появилась как некий Валентин Озеров создал компанию «Кликвуд» по производству деревянных мышей ручной работы. Статья называется «Деревянная мышь. История проекта» и читается на одном дыхании. Очень впечатлило.
Там весь процесс создания, от пластилинового прототипа до рабочего образца.
А вот что не впечатлило и расстроило, так это следующий кусок:
Порой кажется, что при создании изделия кабель — самая несущественная часть изделия. Чего проще — купить в магазине нужное количество кабеля и распаять его. Плевое дело. Но, увы, не у нас в России. Порой кажется, что у нас промышленность уже не приспособлена делать ничего сложнее чугунных утюгов. Попытки найти кабель вылились в трехнедельные поиски и перетряхивание ассортимента абсолютно всех производителей российской кабельной продукции…
Итог: пытался списаться, наверное, с десятком возможных производителей кабельной продукции, никому не интересно возиться с моим заказом. Даже не спрашивали, какой километраж мне нужен. Итог: в России такой кабель не купить и не произвести. Грустно. Но мы не привыкли отступать.
Иду на Alibaba.com. Нахожу первого попавшегося китайского производителя, пишу письмо и буквально через несколько часов получаю ответ: сделаем для вас любой кабель! Я в шоке. Кидаю ему спецификацию, денег на доставку, и через неделю получаю образец. Вот это да! А я почти три месяца потерял, пытаюсь патриотично разместить заказ в России. Оказалось, что китайцы совершенно спокойно могут сделать мне кабель с внешним диаметром 2,5 мм.
Китайцы в кратчайшие сроки сделали ему кабель по его спецификации, прислали несколько образцов, довели кабель до ума. В России даже не попытались прикинуть выгодно ли будет этим заняться. Выводы сделайте сами.
Генерируемый задний фон
Я
10 PRINT CHR$(205.5+RND(1));: GOTO 10Выводит вот такой фон (скриншот с «Командора 64», поэтому цвета такие):

Для тех, кто не понимает этот архаичный язык, перепишу на Пайтоне:
import random; while 1: print random.choice(u'╱╲'),и ПХП:
for(;;) flush(print '/\\'[mt_rand(0, 1)+.5]);На «Хабре» статья пробегала, где автор пытался сделать то же при помощи CSS, не справился и сделал генерацию на серверной стороне — на PHP. Он высказывал мысль использовать для этого JS, но то ли не смог, то ли отбросил её
В общем, решил я попробовать сделать это на стороне клиента, на «канвасе», заодно посмотреть конструкции, которые позволяют ставить «канвас» в качестве задника, давно руки чесались.
Сначала результат (в левом верхнем углу фрагмент нормального размера):

Теперь посмотрим код. Я убрал всё лишнее, подробности можно посмотреть в разделе «Храню»:
!function(c) {
var ctx = document.getCSSCanvasContext ?
document.getCSSCanvasContext("2d", "maze", c.width, c.height):
c.getContext("2d");
ctx.font = "10px monospace";
ctx.fillStyle = '#36f';
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = '#068';
for (var y = 7, ly = c.height; y < ly; y += 8) {
for (var x = -1, lx = c.width; x < lx; x+= 4) {
ctx.fillText("/\\".charAt(Math.random() + .5), x, y);
}
}
if (!document.mozSetImageElement && !document.getCSSCanvasContext) {
document.getElementsByTagName('body')[0].style.backgroundImage='url(' + c.toDataURL("image/png") + ')';
}
}(document.getElementById('maze'));Внутрь функции у меня передаётся объект «канваса» (размером 99×96, подогнан под шрифт).
В первой строке проверяется наличие метода «getCSSCanvasContext», он работает только в браузерах, основанных на Вебките (Хром, Сафари) и позволяет напрямую использовать «канвас» для задника, нужно только в CSS указать идентификатор:
body { background: -webkit-canvas(maze); }Если этот метод недоступен, то я рисую в скрытый тег «канваса», который есть у меня на странице. Вывожу те же самые символы, которые использовались в Бейсике.
Связь «канваса» и задника для браузеров на «Геко» (ФайрФокса и менее известных) в Джаваскрипте не видна, она полностью на стороне CSS:
body { background: -moz-element(#maze); }Эти браузеры позволяют указать в качестве задника что угодно — хоть IFRAME, был бы у него идентификатор. Соответственно, «maze» — идентификатор моего «канваса». Делать больше ничего не нужно, любой элемент с этим «айди» размножится в качестве фона.
В последних строках я проверяют доступен ли метод для указания «-
Комикс: круги
Давно

С козулей из Ижевска
По приезде в Ижевск (это столица Удмуртии) тут же обнаружил новое интересное слово. Скажите, с чем у вас ассоциируется слово «козуля»? У меня — с соплями. Может быть это только у нас в Татарстане соплю козулями называют (в детстве, как правило, мы говорили «козюля»), не знаю.
В общем, я был немало смущён когда в отеле «Центральный» Ижевска увидел кучу предложений купить козулю.

Оказалось, что козуля — это «традиционный фигурный пряник со специями», думаю, на скриншоте вы это уже самими видите. Так что, жена, я тебе из Ижевска козулю везу!
За ру́лем? За рулём?
Помните байку, что в годы войны,
Другой вариант легенды — Сталину на подпись принесли список генералов, в фамилиях некоторых должна была быть буква «ё», но её не напечатали. Сталина это
На мой взгляд, это
Вот посмотрите, 1940 год, Советский Союз ещё не воюет, журнал «За рулём», начиная со своего появления в 1928 году, пишет себя через «е»:

Далее в архиве провал и следующий год — 1956, появляется буква «ё». Что интересно, в этом году появились новые «Правила русской орфографии и пунктуации», которые делают букву «ё» факультативной во многих случаях.

Так продолжается вплоть до 1975, когда журнал возвращается к «оригинальному» названию:

И до нашего времени так и остаётся с «народным» названием (я не раз слышал в деревнях фразу «я за ру́лем уже 35 лет сижу»). Что случилось в 1974 году? Наверное, в журнал пришёл
Добавлено: при внимательном рассмотрении оказалось, что буква «ё» пропадала так же в период с 1965—1967 гг.
Раскалённый мост
Наткнулся на статью, где вскользь упоминается, что Калинов мост через речку Смородину вовсе не приятное место, как может показаться из названия. Сейчас я смутно вспоминаю, что
В общем, Калинов он не от слова «калина», а то слова «раскалённый», а речка Смородина ничего общего с ягодами не имеет, название происходит от слова «смрад». То есть, это раскалённый мост через
Отделяет этот мост, ни много ни мало, царство живых и царство мёртвых и охраняется Трёхглавым Змеем.
В этой связи, название одной из любимых мною музыкальных групп, «Калинов Мост» предстаёт
Идея про скроллинг
В который раз, проматывая фотки в браузере, ловлю себя на одной и той же мысли — очень хочется, чтобы при нажатии пробела браузер проматывал до следующего фото.
Казань → Ижевск → Казань
1 и 2 декабря буду в Ижевске по делам. Прощайте, выходные, я буду скучать!
Масштабирование тега CANVAS
Думаю, многие знают, что у тега CANVAS можно задать размер, а потом смасштабировать его до нужных размеров при помощи CSS.
<canvas width="10" height="10" style="width:100px; height:100px"></canvas>В этом случае, он не меняет свой размер, доступный из кода, но на экране растягивается или сжимается до указанных величин. Иногда это бывает полезно, но есть недостаток — все браузеры, в которых я это пробовал, сглаживают («размывают») содержимое этого тега, что иногда нежелательно.
У меня нет возможности посмотреть под всеми распространёнными версиями браузеров, но, как выяснилось в эксперименте, с современными работает такой трюк, можно указать алгоритм масштабирования для изображений, который для «канваса» тоже работает:
canvas {
image-rendering: optimizeSpeed; /* old WebKit, Safari, FireFox 3.6+ */
image-rendering: -webkit-optimize-contrast; /* newer WebKit (CSS3 proposal) */
image-rendering: -o-crisp-edges; /* Opera 11.6+ (CSS4 prefixed) */
image-rendering: -moz-crisp-edges; /* FF 6+ (CSS4 prefixed) */
image-rendering: crisp-edges; /* CSS4 */
}
Чтобы была понятно о чём вообще речь — слева на скриншоте версия без сглаживания (с применёнными стилями), справа — со сглаживанием, это я прочертил одну линию:
with (canvas.getContext('2d')) {
moveTo(0, 0);
lineTo(10, 10);
stroke();
}Я снабдил код комментариями, чтобы было видно где это работает. К сожалению, в Эксплорере ничего не получится,
Добавлено позднее: похоже, у Хрома тоже проблемы. На разных ОС может работать или нет.
Шутка про рекурсию
Последовательно выписал из «Википедии» определения дней недели из статей, что получилось:
Понеде́льник — день недели между воскресеньем и вторником. Вто́рник — день недели между понедельником и средой. Среда́ (от средина, середина) — день недели между вторником и четвергом. Четве́рг — день недели между средой и пятницей. Пя́тница — день недели между четвергом и субботой. Суббо́та (от ивр. שַׁבָּת шабба́т) — день недели между пятницей и воскресеньем. Воскресе́нье — день недели между субботой и понедельником.
Предки и архив
Неожиданно выяснилось, что некоторые читатели весьма заинтересовались моими исследованиями родового древа и занялись тем же: я получаю регулярно письма с определёнными вопросами, один из которых встречается настолько часто, что нужно, наверное, на него в блоге ответить. Сразу предупреждаю, я не специалист, то что я пишу по этой теме — плод моих размышлений, опыта и знаний, полученных на форумах.
Прошлая моя статья по этой теме была про один из способов узнать откуда родом ваши предки. Читатели спрашивают — даёт ли это знание
Да, конечно даёт, имея место рождения можно предположить в какой архив обратиться, чтобы провести
На основании информации о том, где родились мои бабушки и дедушки, я могу предположить с большой вероятностью где родились мои прадеды и прабабушки и запросить об этом архив. Дело в том, что никакого единого архива не существует, у каждого региона свои архивы. Бывают ещё архивы районов, которые находятся в районом центре. В Татарстане архивы районов мне пока оказываются полезны меньше, чем архив региона (Национальный Архив Республики Татарстан), но я, тем не менее, обращаюсь и в архивы районов тоже — документы часто горели в прошлом, часть материалов утрачена и совершенно неясно где что могло сохраниться, нет закономерности.
Так же имеет смысл обращаться в архив региона, куда в исследуемое время относился рассматриваемый регион. В моём случае, это Бугульминский район, который сейчас относится к Татарстану, а в разное время относился (как уезд) к Самарской, Оренбургской губернии и Уфимской области.
Перед тем, как обращаться в архив, следует решить к какой церкви относился населённый пункт, который вы изучаете. Если это город, то в нём скорее всего была церковь (но это всё равно не мешает проверить), в случае села или деревни это вовсе необязательно. Более того, если в селе нашлась церковь, следует узнать когда она была построена, так как до этого времени село могло относиться к другой церкви. Эту информацию сейчас легко найти в интернете, РПЦ, как правило, тщательно документирует эту информацию и тайны в ней никакой нет.
Насколько я знаю, сёла и деревни раньши различались по этому принципу — была церковь или нет, но сейчас с этим полная путаница.
Я сейчас говорю, конечно, о тех, кто исповедовал провославную веру или считался таковым, как дела обстоят с поиском предков исповедующих другую веру, я не знаю. Церкви важны именно потому, что все важные вещи (рождение, смерть, брак) записывались именно в церковные книги.
Как узнать к какой церкви принадлежал населённый пункт? Кроме справочников и карт эту информацию почерпнуть больше неоткуда. Пока все такие справочники и карты мне удавалось найти в интернете. Давайте я покажу каким образом по карте можно понять к какой церкви мог бы принадлежать определённый населённый пункт. Возьмём для примера село Дубовка, где родилась одна из моих прабабушек.

Если сравнить карту Бугульминского района Татарстана с картой Бугульминского уезда Самарской губернии, то мы обнаружим, что современному селу Дубовка соответствует деревня Мирная Дубовка на старой карте. В границах Бавлинской волости, куда она относилась, всего одна церковь (она помечена крестом, мечети помечены полумесяцем), из чего можно сделать вывод, что Малая Дубовка относилась к селу Потапова Тумбарла. В других случаях, это скорее всего будет ближайшая церковь.
Вооружённый этими данными, я могу написать запрос в архив по родителям своей прабабушки, с просьбой найти по ним сведения, пользуясь данными прихода села Потапова Тумбарла.
Теннис на CSS-анимациях

Я тут решил прикинуть для себя недостатки CSS-анимации, поэтому написал «теннис» на HTML и CSS. В кавычках, потому что играть в него невозможно: можно лишь управлять при помощи мышки «ракеткой», а шарик в этот момент будет летать из угла в угол, ни на что не реагируя.
Я было начал писать программу, которая рассчитает мне более длинную траекторию мяча, чтобы соударения выглядели повеселее, но поленился обработать все восемь комбинаций соударения, хотя вспомнить тригонометрию было весело.
Префиксы для всех браузеров в коде ставить не стал, оставил только для «вебкита» (будет работать в «Сафари», «Хроме» и «Яндекс.Браузере»), всё равно все браузеры собираются начать их поддерживать, так зачем же стараться?
Так вот, анимации в ЦСС — интересная штука, позволяющая простыми средствами плавно менять свойства объектов на странице, мне понравилось. Но у неё есть недостаток, на мой взгляд — анимируется ровно один элемент и у неё нет состояний.
Т. е. я не могу посмотреть в каком состоянии сейчас находится анимированный элемент, чтобы привязать к этому событию
По этой причине я не смог написать полноценный теннис — у меня просто нет средств, чтобы связать между собой положение мяча и ракетки.
Из других найденных недостатков — далеко не все свойства нормально анимируются, например, с фоновыми картинками
Кстати, гениальные ребята, которые для Интернет Эксплорера
Казань → Ульяновск → Казань
24 ноября, в субботу, буду в Ульяновске по делам.
Наведение порядка в PHP (версия 5.5.0)
Нужно ещё похвалить авторов PHP за то, что они действительно стараются навести порядок в языке. С каждой версией он становится немного логичнее.
Например, теперь уже можно сделать вот так:
[
POW => function($x) { return $x*$x; },
SIN => function($x) { return sin($x); },
][$selector]Правда, всё ещё нельзя вызвать функцию сразу. В 5.5.0 будет работать вот такая вещь:
if (empty(somefunc())) { /* … */}Но то же самое нельзя сделать с isset, так как сложно сказать что это будет значить — в языке нет отдельного неопределённого значения переменной, она бывает только недекларированной; функция, конечно, может её вернуть, но на самом деле вернётся NULL, а isset от значения NULL — «ложь».
Появилась возможность использовать list внутри цикла следующим образом:
foreach ($somehash as list($key, $value)) { /* … */ }И сделано ещё несколько мелочей, но работы впереди ещё много.
Генераторы в PHP 5.5
Самым большим нововведением (и очень важным для языка) в PHP 5.5.0 будут генераторы. Я установил себе
Посмотрите на код, который я написал, пока мы ждали всех участников встречи, на которую я был приглашён:
<?
function enumerate($arr)
{
$i = 0;
foreach ($arr as $value) {
yield [$i++, $value];
}
}
function ifilter(callable $predicate = null, $arr)
{
if ($predicate === null) {
$predicate = 'boolval';
}
foreach ($arr as $value) {
if ($predicate($value)) {
yield $value;
}
}
}
function islice($arr, $start, $stop)
{
if (is_array($arr)) {
reset($arr);
for ($i = $start; $i > 0 && each($arr); $i--);
for ($i = $stop - $start + 1; $i > 0 && list(,$value) = each($arr); $i--) {
yield $value;
}
} else {
for ($i = $start; $i > 0 && $arr->valid(); $i--) {
$arr->next();
}
for ($i = $stop - $start; $i > 0 && $arr->valid(); $i--) {
yield $arr->current();
$arr->next();
}
}
}
$array = [10, 20, 30, 33, 40, 50, 60];
$gen = islice(
enumerate(
ifilter(
function($x) { return ($x % 10) === 0; },
$array
)),
1, 4
);
foreach ($gen as $value) {
print_r($value);
}Три генератора, пропускающие через себя данные, обрабатывают массив, всё работает. Названия взяты из Пайтона. Первый нумерует входящий поток данных (возвращает пары номер и значение), второй фильтрует значения через функцию, третий отрезает заданную часть от входных данных.
Концепция генераторов проста, как герань. PHP поддерживает два способа их создания (как и Пайтон), но мне милее оператор yield. Работает он примерно как return, но при повторном входе в функцию её выполнение происходит не сначала, а с того места, откуда управление было возвращено при помощи yield. Можно ещё передавать значения внутрь генератора, но я про это сейчас не буду. Вцелом, получаются настоящие ленивые вычисления — всё это вызывается ровно тогда, когда это реально нужно.
Генераторы, обычно, работают с итерируемыми объектами — возращают их и/или принимают. Итерируемый объект, проще говоря, это то, по чему можно пробежаться циклом. Генераторы, кстати, тоже итерируемые.
И вот тут опять проявляются недостатоки PHP: отсутствие общей логики, непродуманность.
Массив тут до сих пор примитивный тип и он требует иной способ обработки. Посмотрите на генератор islice, тут проблема видна во всей красе. Начинается она уже с хинтинга, я не могу потребовать от $arr быть типа array, потому что на вход может прийти другой генератор, и указать класс генератора тоже не могу — массив ему не принадлежит. Думаю, следует ожидать появления нового типа «iterable», как это уже случилось с callable.
Вторая проблема ещё более серьёзная. У массива нет методов (он же примитивный тип), а генераторы,
Мне кажется, PHP следует развивать класс ArrayObject, который уже присуствует в языке и делать прозрачное преобразование примитивных типов в объекты (как это происходит в JavaScript), это решило бы некоторые проблемы.
Депиляция и эпиляция
Постоянно вижу на вывесках слова «эпиляция» и «депиляция» вместе, думаю, многие считают их синонимами. Проезжая сегодня мимо очередной такой вывески, решил заглянуть в словарь и разобраться, есть разница или нет.
Так вот. То и другое про удаление волос, дальше детали. Депиляция — это удаление видимой части волоса, например, мужчины по утрам занимаются депиляцией, если не ленятся, а эпиляция — удаление волоса целиком, вместе с луковицей. Например, выдёргивание волоса пинцетом это эпиляция.
Ещё раз: депиляция — только часть волоса, эпиляция — целиком.