Разворот односвязного списка за 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, остальное — заполнение структуры и вывод на печать. Оцените как мало надо кода, чтобы решить это задание.

5 комментариев

Angry Spy

Что-то я ничего не понимаю. Зачем приложению «Angry Birds Roost» нужны следующие полномочия на моём смартфоне?

Требуются: встречи, контакты, удостоверение телефона, удостоверение владельца, съёмка фото и видео, службы определения местоположения, воспроизведение мультимедия, микрофон, службы данных, служба push-уведомлений, датчик движения и направления

Зачем это всё (разве что кроме «службы push-уведомлений») бесплатному агрегатору мусора про разъярённых птиц?

4 комментария

Возраст объектива Кэнона

Какие-то ребята утверждают, что раскрыли нехитрый алгоритм, который позволяет вычислить возраст фирменного объектива Кэнона.

Возраст зашифрован в первых двух цифрах десятизначного серийного номера, который должен выглядеть так: ДД П ССССССС, где «ДД» — код даты, «П» — скорее всего номер партии, а «ССССССС» — уникальный номер объектива.

Вбейте в строку адреса браузера следующий код:

javascript:isNaN(n=prompt('Введите первые две цифры номера вашего объектива:')-61)||alert((n+=(n<6)*100)%2512+'/'+~~(2010+n/12))

нажмите «Enter», введите в появившееся окно первые две цифры с объектива, снова нажмите «Enter», получите дату изготовления.

9 комментариев

Из документации по «Сфинксу»…

Из документации по поисковому движку «Сфинкс» (смешную фразу я выделил):

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.

6 комментариев

Потрясающая история деревянной мыши

На «Хабре» статья появилась как некий Валентин Озеров создал компанию «Кликвуд» по производству деревянных мышей ручной работы. Статья называется «Деревянная мышь. История проекта» и читается на одном дыхании. Очень впечатлило.

Там весь процесс создания, от пластилинового прототипа до рабочего образца.

А вот что не впечатлило и расстроило, так это следующий кусок:

Порой кажется, что при создании изделия кабель — самая несущественная часть изделия. Чего проще — купить в магазине нужное количество кабеля и распаять его. Плевое дело. Но, увы, не у нас в России. Порой кажется, что у нас промышленность уже не приспособлена делать ничего сложнее чугунных утюгов. Попытки найти кабель вылились в трехнедельные поиски и перетряхивание ассортимента абсолютно всех производителей российской кабельной продукции…

Итог: пытался списаться, наверное, с десятком возможных производителей кабельной продукции, никому не интересно возиться с моим заказом. Даже не спрашивали, какой километраж мне нужен. Итог: в России такой кабель не купить и не произвести. Грустно. Но мы не привыкли отступать.

Иду на Alibaba.com. Нахожу первого попавшегося китайского производителя, пишу письмо и буквально через несколько часов получаю ответ: сделаем для вас любой кабель! Я в шоке. Кидаю ему спецификацию, денег на доставку, и через неделю получаю образец. Вот это да! А я почти три месяца потерял, пытаюсь патриотично разместить заказ в России. Оказалось, что китайцы совершенно спокойно могут сделать мне кабель с внешним диаметром 2,5 мм.

Китайцы в кратчайшие сроки сделали ему кабель по его спецификации, прислали несколько образцов, довели кабель до ума. В России даже не попытались прикинуть выгодно ли будет этим заняться. Выводы сделайте сами.

19 комментариев

Генерируемый задний фон

Я как-то пропустил знаменитую (оказывается) программу для генерации фона, похожего на лабиринт из двух слешей. Несложная программа на Бейсике:

10 PRINT CHR$(205.5+RND(1));: GOTO 10

Выводит вот такой фон (скриншот с «Командора 64», поэтому цвета такие):

Фон из слешей (27.66КиБ)

Для тех, кто не понимает этот архаичный язык, перепишу на Пайтоне:

import random; while 1: print random.choice(u'╱╲'),

и ПХП:

for(;;) flush(print '/\\'[mt_rand(0, 1)+.5]);

На «Хабре» статья пробегала, где автор пытался сделать то же при помощи CSS, не справился и сделал генерацию на серверной стороне — на PHP. Он высказывал мысль использовать для этого JS, но то ли не смог, то ли отбросил её по каким-то иным причинам.

В общем, решил я попробовать сделать это на стороне клиента, на «канвасе», заодно посмотреть конструкции, которые позволяют ставить «канвас» в качестве задника, давно руки чесались.

Сначала результат (в левом верхнем углу фрагмент нормального размера):

Фон в виде лабиринта (31.46КиБ)

Теперь посмотрим код. Я убрал всё лишнее, подробности можно посмотреть в разделе «Храню»:

!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» — идентификатор моего «канваса». Делать больше ничего не нужно, любой элемент с этим «айди» размножится в качестве фона.

В последних строках я проверяют доступен ли метод для указания «-moz-element», нет ли метода для рисования «канвасом» прямо в задник, если нет, то перевожу картинку из «канваса» в dataURI и добавляю её в таком виде на фон тега BODY. Таким образом, этот код работает и для «Оперы» с «Эксплорером» (начиная с девятого).

7 комментариев

Комикс: круги

Давно комикс-стрипы не советовал. Новое-свежее, жижист с ником Как Всё Глупо начал рисовать комиксы, верх минимализма: одни круги и надписи. Мне нравится, рекомендую. Интересно, когда сюжеты кончатся?

Комикс «Круги» (2.87КиБ)

1 комментарий

С козулей из Ижевска

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

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

Купи козулю (91.20КиБ)

Оказалось, что козуля — это «традиционный фигурный пряник со специями», думаю, на скриншоте вы это уже самими видите. Так что, жена, я тебе из Ижевска козулю везу!

15 комментариев

За ру́лем? За рулём?

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

Другой вариант легенды — Сталину на подпись принесли список генералов, в фамилиях некоторых должна была быть буква «ё», но её не напечатали. Сталина это почему-то очень разозлило и концовка примерно та же.

На мой взгляд, это всё-таки байки (хотя приказ об обязательном употреблении буквы «ё» в школьной практике и появился в 1942), я о них вспомнил, когда листал обложки журнала «За рулём» с 1928 года по наши дни.

Вот посмотрите, 1940 год, Советский Союз ещё не воюет, журнал «За рулём», начиная со своего появления в 1928 году, пишет себя через «е»:

«За рулем» (77.53КиБ)

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

«За рулём» (92.26КиБ)

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

снова «За рулем» (103.11КиБ)

И до нашего времени так и остаётся с «народным» названием (я не раз слышал в деревнях фразу «я за ру́лем уже 35 лет сижу»). Что случилось в 1974 году? Наверное, в журнал пришёл какой-то редактор-реформатор.

Добавлено: при внимательном рассмотрении оказалось, что буква «ё» пропадала так же в период с 1965—1967 гг.

8 комментариев

Раскалённый мост

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

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

Отделяет этот мост, ни много ни мало, царство живых и царство мёртвых и охраняется Трёхглавым Змеем.

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

9 комментариев

Идея про скроллинг

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

18 комментариев

Казань → Ижевск → Казань

1 и 2 декабря буду в Ижевске по делам. Прощайте, выходные, я буду скучать!

6 комментариев

Масштабирование тега 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();
    }

Я снабдил код комментариями, чтобы было видно где это работает. К сожалению, в Эксплорере ничего не получится, когда-то у него было свойство -ms-interpolation-mode, но с девятой версии (где и появился «канвас») оно уже не работает, придётся решать проблему через JS.

Добавлено позднее: похоже, у Хрома тоже проблемы. На разных ОС может работать или нет.

Оставить комментарий

Шутка про рекурсию

Последовательно выписал из «Википедии» определения дней недели из статей, что получилось:

Понеде́льник — день недели между воскресеньем и вторником. Вто́рник — день недели между понедельником и средой. Среда́ (от средина, середина) — день недели между вторником и четвергом. Четве́рг — день недели между средой и пятницей. Пя́тница — день недели между четвергом и субботой. Суббо́та (от ивр. שַׁבָּת шабба́т‎) — день недели между пятницей и воскресеньем. Воскресе́нье — день недели между субботой и понедельником.

12 комментариев

Предки и архив

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

Прошлая моя статья по этой теме была про один из способов узнать откуда родом ваши предки. Читатели спрашивают — даёт ли это знание что-то для дальнейшего исследования рода, если даёт, то что.

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

На основании информации о том, где родились мои бабушки и дедушки, я могу предположить с большой вероятностью где родились мои прадеды и прабабушки и запросить об этом архив. Дело в том, что никакого единого архива не существует, у каждого региона свои архивы. Бывают ещё архивы районов, которые находятся в районом центре. В Татарстане архивы районов мне пока оказываются полезны меньше, чем архив региона (Национальный Архив Республики Татарстан), но я, тем не менее, обращаюсь и в архивы районов тоже — документы часто горели в прошлом, часть материалов утрачена и совершенно неясно где что могло сохраниться, нет закономерности.

Так же имеет смысл обращаться в архив региона, куда в исследуемое время относился рассматриваемый регион. В моём случае, это Бугульминский район, который сейчас относится к Татарстану, а в разное время относился (как уезд) к Самарской, Оренбургской губернии и Уфимской области.

Перед тем, как обращаться в архив, следует решить к какой церкви относился населённый пункт, который вы изучаете. Если это город, то в нём скорее всего была церковь (но  это всё равно не мешает проверить), в случае села или деревни это вовсе необязательно. Более того, если в селе нашлась церковь, следует узнать когда она была построена, так как до этого времени село могло относиться к другой церкви. Эту информацию сейчас легко найти в интернете, РПЦ, как правило, тщательно документирует эту информацию и тайны в ней никакой нет.

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

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

Как узнать к какой церкви принадлежал населённый пункт? Кроме справочников и карт эту информацию почерпнуть больше неоткуда. Пока все такие справочники и карты мне удавалось найти в интернете. Давайте я покажу каким образом по карте можно понять к какой церкви мог бы принадлежать определённый населённый пункт. Возьмём для примера село Дубовка, где родилась одна из моих прабабушек.

Дубовка (104.54КиБ)

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

Вооружённый этими данными, я могу написать запрос в архив по родителям своей прабабушки, с просьбой найти по ним сведения, пользуясь данными прихода села Потапова Тумбарла.

15 комментариев

Теннис на CSS-анимациях

CSS-теннис (26.61КиБ)

Я тут решил прикинуть для себя недостатки CSS-анимации, поэтому написал «теннис» на HTML и CSS. В кавычках, потому что играть в него невозможно: можно лишь управлять при помощи мышки «ракеткой», а шарик в этот момент будет летать из угла в угол, ни на что не реагируя.

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

Префиксы для всех браузеров в коде ставить не стал, оставил только для «вебкита» (будет работать в «Сафари», «Хроме» и «Яндекс.Браузере»), всё равно все браузеры собираются начать их поддерживать, так зачем же стараться?

Так вот, анимации в ЦСС — интересная штука, позволяющая простыми средствами плавно менять свойства объектов на странице, мне понравилось. Но у неё есть недостаток, на мой взгляд — анимируется ровно один элемент и у неё нет состояний.

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

По этой причине я не смог написать полноценный теннис — у меня просто нет средств, чтобы связать между собой положение мяча и ракетки.

Из других найденных недостатков — далеко не все свойства нормально анимируются, например, с фоновыми картинками какая-то беда, но я это списываю на сырость технологии.

Кстати, гениальные ребята, которые для Интернет Эксплорера когда-то придумали большую часть веб-технологий, которые мы под другими именами только недавно получили в виде стандартов W3C, первыми задумались и об анимации в вебе без ДжаваСкрипта. Сначала это был примитивный тег MARQUEE, появившийся аж во второй версии браузера, позже — технология HTML+TIME, позволявшая создавать очень сложную анимацию при помощи специальной разметки, это было позднее — в 5.5, но это было 12 лет назад!

2 комментария

Наведение порядка в 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)) { /* … */ }

И сделано ещё несколько мелочей, но работы впереди ещё много.

8 комментариев

Генераторы в PHP 5.5

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

Посмотрите на код, который я написал, пока мы ждали всех участников встречи, на которую я был приглашён:

<?
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), это решило бы некоторые проблемы.

6 комментариев

Депиляция и эпиляция

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

Так вот. То и другое про удаление волос, дальше детали. Депиляция — это удаление видимой части волоса, например, мужчины по утрам занимаются депиляцией, если не ленятся, а эпиляция — удаление волоса целиком, вместе с луковицей. Например, выдёргивание волоса пинцетом это эпиляция.

Ещё раз: депиляция — только часть волоса, эпиляция — целиком.

2 комментария