Pull to refresh

Comments 419

Я бы javascript вынес из тегов в заголовок, иначе когда доходит до задачи, то возникает легкое недоумение…

А ещё лёгкое недоумение тут вызывают методы собеседования автора. Приходишь ты такой на собеседование, а тебя спрашивают о знании стандарта, который позволяет поменять зелёную и коричневую какашки из юникода местами. [::-1] тоже, внезапно, не переварачивает html-страницу вверх ногами, не возвращает аргументы javascript-кода по return-value и не делает меня молодым: так может ещё об этом спросить?

Не стоит. В Ruby метод reverse выдаёт предсказуемый результат с указанной кодировкой.
В этом случае автор предлагает бороться с проблемами наивного подхода на выбранном стеке.
Мне кажется, что если эта проблема возникнет в продакшне хотя бы у одного разработчика, появится очередной модуль в npm для корректной манипуляции с Unicode-строками. Или уже есть, но никто не поискал.
оказывается в стандарте юникода есть вариант объединения не только emoji, но и просто символов

Подождите. Я ведь правильно понимаю, что точки над 'ё' — это типичная диакритика, и если ваш алгоритм не работает с 'ё', то он также сломается на «й» (и + ˘) и скорее всего не работает с банальными немецким и французским?
Неужели эмодзи используются настолько более часто, чем диакритика?

Я немного ввел в заблуждение, простите. Речь идет не о букве "Ё", а о сочетании 2х символов Юникода u{0415}(Е) и u{0308}("̈), которые идя друг за другом образуют последовательность юникода и мы видим букву "Ё" на экране.


\u{0415}\u{0308}' === 'Ё' // false
Лучше смешанный c RLM и LRM, тут и нормализация не поможет :)
Prelude> no = "لا"
Prelude> putStrLn $ reverse no
ال
Prelude> putStrLn $ reverse $ reverse no
لا
Prelude> putStrLn $ reverse $ reverse ("Торт " ++ no ++ " Habr")
Торт لا Habr
Prelude> putStrLn  $ reverse ("Торт" ++ no ++ " Habr")
rbaH ال троТ

Люблю нормальные, продуманные языки.

Вопрос не а языке, а в библиотеке. С тем же успехом


pub fn reverse(input: &str) -> String {
    UnicodeSegmentation::graphemes(input, true).rev().collect()
}
Это вполне себе нормальная и, местами, даже типичная буква 'Ё'! Есть платформы, которые нормализуют Unicode к NFC, например, Windows, на них 'Ё' обычно один символ. А есть платформы, которые нормализуют к NFD, например, macOS, там 'Ё' обычно два символа.

И за это разработчиков макос хочется больно пинать ногами

Хочется, не хочется, вопрос личный. А вот Линуса нашего Торвальдса и компанию обязательно! 21 век на дворе, а файловые системы Linux поддерживают не Unicode, а хрен знает что (набор байтов и граблей):
[user@linux ~]$ ls -l /tmp/у*
-rw-r--r-- 1 leo staff 31415 апр 11 23:02 /tmp/уй
-rw-r--r-- 1 leo staff 271828 апр 11 22:57 /tmp/уй

К стати, выбор нормализации NFD, принятый в HFS+ обеспечивает наискорейшее обнаружение ошибок работы с файлами в приложениях. Впрочем и NFС неплох, но не так как в Linux это ж точно.
Они как раз не занимаются всякой самодеятельностью, а пишут байты как передали.
И это приводило, приводит и будет приводить к труднообнаружимым глюкам и проблемам с безопасностью. Пока не сделают как все нормальные люди на них клеймо.

P.S. Например, habr нормализовал по NFD UTF8 результаты ls и один из этих файлов стал «недоступен». Мало того, ключи ls '-q' или '-b' не изменяют выдачу, т.к. считают значок «кратка» печатным символом.
Нет уж, кушайте ваши какашки нормализованные сами. Нормализация ещё приемлима в human-to-human интерфейсах, когда машине не нужно интерпретировать текст. В машиночитаемых интерфейсах нормализация неприемлима, ибо она приведёт к ещё более огромной куче проблем и несовместимостей. Как байты записаны — так должны быть и прочитаны, точка. Хумансы для себя должны реализовать удобные средства различения этих байтов — тут не поспоришь.
Файловая система, либо поддерживает Unicode, либо нет, если поддерживает, то должна обеспечивать нормализацию. Скажем, ZFS (Solaris, FreeBSD) поддерживает, NTFS (Windows) поддерживает, HFS+ поддерживает (macOS).

А подход файловых систем Linux — набор байтов, это набор граблей.
Набор граблей — это когда я записал одни байты, потом прочитал из того же места байты и сравнил с имеющимися у себя. Что дальше? Дальше — исключения, сегфолты или вообще heartbleed, если у нас ФС отнормализовала текст, или всё работает as expected, если не нормализовала.
Куда записал? Имя файла, это не место для записи байт, это «имя файла». Кодируется в URL так, человеку показывается этак и т.п. Плюс обязательная возможность задания образцов поиска, удобных для человека. В половине систем оно вообще нечувствительно к регистру символов, и имеет формы зависящее от языка текущего пользователя.

Место для записи байт — бинарный файл.
Ну так пусть инструмент, занимающийся поиском и нормализует (во всех понятиях этого слова)! Зачем здесь ФС-то приплетать?

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

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

Затем, что когда ФС нормализует имена файлов, весь (повторяю, весь) софт, работающий с этой ФС, тоже обязан их нормализовать — иначе кровькишки. Если ФС не нормализует — у нас есть выбор. Для вынесения функционала нормализации можно использовать, например, библиотеки.
Вот с чего бы «тоже обязан их нормализовать»? Например, с прошлого века большинство систем имеют ФС с именами, которые не зависят от регистра. Как-то без «кровькишки» обходимся.

Так же как и не одно десятилетие работаем с ФС, у которых имена с разной нормализацией Unicode, а так же без нормализации Unicode гарантировано означают один и тот же файл. И тоже без «кровькишки» обходимся.

В чём проблема?
Без «кровькишки», говорите? Первое, что вспомнилось (и что очень больно пнуло меня, когда приходилось подрабатывать вебдевом): github.com/jprichardson/node-fs-extra/issues/565

Сейчас ссылок не дам, но точно вам говорю: кровькишки регулярно встречаются. Особенно, когда привык к идеологии «что прочитал — то и записал». Возможно, это деформация линуксоидов какая-то :)
Не деформация, детская болезнь, через это многие системы прошли. Мне так кажется.

Встречаются же POSIX системы c EBCDIC именами файлов и locale (z/OS), где даже ASCII — бинарные файлы и невозможные имена. Поэтому инсталляторы Java приложений и jar архивы нужно делать не абы как, а в строгом соответствие со стандартами Java.

Даже если это болезнь, от неё никуда уже не убежишь. Linux прочно закрепился в истории человечества как одна из самых популярных ОС (в широком смысле слова). Менять в нём стандарт сейчас, а не пятнадцать лет назад — уже поздновато. Смена фс на case-insensetive или нормализующую юникод сломает тысячи приложений и миллионы строк кода. Кто будет это чинить?


Для осознания масштабов поломки, можете попробовать сделать C:\ case-sensetive и не-нормализующей.

Многие прошли через это. Не проблема, ни сейчас, ни в будущем. Рано ли поздно это неизбежно произойдёт, я так думаю.

«Смена фс на case-insensetive или нормализующую юникод сломает тысячи приложений и миллионы строк кода. Кто будет это чинить?» — linux подсистема Windows является ubuntu и глючит не больше оригинального ubuntu.

Отключить нормализацию на C:\ — тоже без проблем, гарантировано.

P.S.
Ну по популярности, в форме Android — да ;)
> Смена фс на case-insensetive или нормализующую юникод сломает тысячи приложений и миллионы строк кода.

А что, например, с FAT эти миллионы строк до сих пор не умеют? И вы не считаете это проблемой?
Не умеют и не считаю. Потому что с FAT даже и Windows 10 «не умеет» (работать с флешками — умеет, встать на FAT — не может).

В Linux — то же самое.
Справедливости ради, устанавливаться с FAT-носителя винда умеет.
Не поддерживать тома FAT больше 32Гб — это не «не умеет», а волевое решение создателей винды. Собственно, до висты винда в 32 Гб помещалась и всё умела, потом просто отключили, ибо нефиг.

Нет, миллионы строк нерабочего кода и if'чик в инсталляторе — не то же самое.
UFO just landed and posted this here
> десятка прекрасно умещается на 16ГБ.

Ну как прекрасно… Без ничего, с отключенными гибернацией и файлом подкачки? А если ещё и сжатие включить…
Ну, несерьезно.

Раньше вместо симлинков ярлычки были, если помните. Если бы не отказались от FAT — и сейчас бы были. Но да, тоже повод закопать стюардессу.
UFO just landed and posted this here
> Совсем другая сущность.

сушность может и другая, а костылили на ней
UFO just landed and posted this here
Отвечал с утюга…
FAT32, вообще-то бывает аж до 2 терабайт. Windows 98 отлично вам такое создаст, а Windows 10 не создаст (GUI-тулы имеют ограничения), но прочитает. Дело не в объёмах.
Простите, а какое слово в «волевое решение создателей винды» вам не понятно?
Жизнь покажет, думаю и Linux, рано или поздно, тоже придёт к Unicode в файловых системах.

Нет, конечно, можно в ФС ничего не делать, а требовать от всех приложений что б они все идентично нормализовали имена сами ;) Как это сейчас в Linux и происходит, что и позволяет более менее сносно жить. Но такой подход как раз и является тем самым набором граблей ;)

P.S. А ошибки переполнения буферов в неправильных программах они завсегда были и ещё долго будут, причём, что при работе с «вашими» наборами байт, что при работе с нормальными именами Unicode.

Ну тогда объявляем примерно весь софт под Linux "неправильным" — он ведь опирается на предположение, что читает то же самое, что и пишет.


требовать от всех приложений что б они все идентично нормализовали имена сами

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

Почему «весь софт под Linux неправильный»? Большинство приложений работающих под Linux отлично работают и под Solaris, FreeBSD, macOS и Windows, поскольку они не опираются на неестественное предположение об именах файлов: «что читает то же самое, что и пишет».

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

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

А ещё его можно попытаться поискать в листинге, например. Возникает проблемка, не так ли?

У большинства приложений наоборот, исчезает проблемка. За исключением, ясен перец, выше поименованных исключений.
NTFS (Windows) поддерживает

Как раз NTFS вообще ничего не поддерживает, кроме
регистронезависимости. Для него имя файла — это UCS-2. Попробовал создать два файла: й.txt и й.txt. Прекрасно создались.


А подход файловых систем Linux — набор байтов, это набор граблей.

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


Так что подход Linux я считаю более адекватным.

>Как раз NTFS вообще ничего не поддерживает, кроме
регистронезависимости.
Регистронезависимость это фича Win32 а не NTFS, причем отключаемая фича.
Регистронезависимость это фича Win32 а не NTFS, причем отключаемая фича.

Нет. Регистронезависимость — это атрибут директории (кстати, завезли совсем недавно).


В NTFS директория представляется в виде B-TREE, причём ключами этого двоичного дерева являются преобразованные имена файлов (в рассматриваемом случае — преобразованные в lowercase), а чтобы добраться до реального имени файла, нужно лезть в атрибуты записи. Именно возможность быстрого регистронезависимого поиска и позволяет говорить о регистронезависимости NTFS.


P.S. Так-то аболютно любая ФС может быть и регистро-зависимой, и регистро-независимой. Всё зависит от драйвера ФС.

Регистронезависимость — это атрибут директории (кстати, завезли совсем недавно).
Атрибут директории завезли недавно, флаг в реестре — скорее всего в прошлом веке. Сейчас на Microsoft документацию для Windows NT и даже Windows XP отыскать сложно, но вот тут говорится, что проблема с Case Sensitivity пофикшены в версях .NET 2.0 (sic!), выпущенных после 2006го — а это значит что поддержка явно старше, раз в 2006м (то есть до выхода Windows Vista) её уже фиксили…
Это .net фиксили. Он же не только на винде работает.

А винду чинили в прошлом году только, тогда и флаги привезли все, и ключи.
Это .net фиксили. Он же не только на винде работает.
Как прекрасен этот мир, посмотри… поколение «не рефлексирующих, а распространяющих», похоже, окончательно победило. С учётом того, что вас ещё и плюсуют.

Это у вас «не на винде реестр вдруг появился? С ключом

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\kernel\

dword:ObCaseInsensitive?

Это где же вы такое нашли, интересно? И где вы в 2006м году нашли .NET не под Windows?

Нет, всё это точно было в Windows XP, не удивлюсь если и в Windows NT 3.1 тоже всё было — просто проверить негде.
Надо просто понимать разницу между документированными функциями ОС, и какими-то флажками в реестре. Драйвер ntfs много что умеет, но кроме этого драйвера в винде есть ещё ядро, слой апи для приложений, и сами приложения и их пользователи тоже — часть экосистемы винды. Так вот в винде, как экосистеме, регистрозависимые имена файлов появились в прошлом году. Это потребовало и изменений в Win32, и доработки приложений, и обучения пользователей и много чего ещё.

А в 2003 году был всего лишь недокументированный флаг в реестре, который техподдержка включала для каких-то специфических кейсов отдельных пользователей. Ну и код в драйвере. Но там очень много очень разного кода, не все из этого «винда поддерживает»
Надо просто понимать разницу между документированными функциями ОС, и какими-то флажками в реестре.
А какая разница? Где сегодня, сейчас, в Windows 10 большая кнопка, которая включает/выключает регистрозависимость? Чем команда в PowerShell «интуитивнее» ключа в реестре?

Так вот в винде, как экосистеме, регистрозависимые имена файлов появились в прошлом году.
В винде, как в экосистеме, всё это появилось больше четверти века века назад — в Windows NT 3.1. Где и флажок FILE_FLAG_POSIX_SEMANTICS был (это, блин, вообще первый «расширенный» флаг, который в CreateFile появился) и регистронезависимость была и даже POSIX-подсистема была!

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

Но там очень много очень разного кода, не все из этого «винда поддерживает»
Конкретно регистронезависимость она поддерживала весьма прилично ибо ей нужно было пройти POSIX-сертификацию. Ибо в те времена в госучреждениях разрешалось использовать только POSIX-совместимые OS.
Разница в уровне поддержки и документации, раньше это «есть какой-то флажок, но если что-то сломается, то не обижайся». Как раз для вот таких любителей посикса.

Сейчас это поддерживается во всех стандартных сценариях, и насколько помню, в RS5 они хотели вообще включать это по дефолту в новых установках.
Дык оно и изначально во всех сценариях поддерживалось. Ограничения появились только в Windows 2000 — до этого и вариантов-то не было: каждая программа могла сама решать будет она поддерживать регистронезависимость или нет.

Флажок, когда появился, позволил оптицонально эту поддержку отключать — отменяя флаг FILE_FLAG_POSIX_SEMANTICS, который, ещё раз повторяю, был там с самой первой версии Windows NT.
Ах да, инсталлятор дотнета, похоже, как раз ломал те самые специфические кейсы отдельных пользователей. Видимо, они пользовались не только виндой и им эта регистрозависимость была нужна. Вот инсталлятор и починили в 2006 году.
На самом деле это была классическая схема bait-n-switch, за которую у Microsoft'а много денег отсудили.

Изначально поставляя Windows NT как POSIX-совместимую систему начиная с Windows 2000 Microsoft начал делать попытки заставить всех отказаться от POSIX и перейти исключительно на Win32 API. Извините, но инсталлятор не может «случайно» залезть в реестр и выключить там регистронезависимость. Этот код кто-то должен был в него целенаправленно добавить. Совершенно случайно посреди инсталлятора вполне конкретная строка в 50 символов длиной не возникает.

Но с учётом того, что Microsoft и так в судах трепали в то время изрядно — признать это Microsoft не мог и потому пришлось делать вид, что «ой — да мы ошиблись просто, никто ничего „такого“ и не хотел».

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

Тогда reverse(reverse(s)) !== s

Это нормально. Перед сравнением двух юникодных строк их необходимо нормализовывать.
R̸̩͔̫̠̪̠̪ͤͬ͜I͍̱͉̝̐ͫ̾̅͂̒ͥ͌͟D̵̴̘͙̤͖̪̖̭̲ͦD͔̘̼̫̬͓ͥ̈́̑̂Ľ͇͙͙̠͕ͪ̏̈́E̢̡̡̫̘̔̋͂ ̛̳͚͈̰͙̲̀ͩM̗̘̩̣͉͇ͬ̔ͨͥ̎̕E̛̟̭͖͈̩̐ͩ͗ͩ̎̕ ͖͔̠̳́ͭ͆̏ͪṬ̢͓͈̞͛̕Ḩ̡̣͔͖͎͚̤̻ͥ͒I̓̏̏̈҉̵̳̞̠͍̯S͔̬̙̱̱̝͂ͯ̈́͐̂͜

Как вы это сделали? Расскажите! Интересно!)

T̫̮̟̱̼̭͝o̸̹͉͇͓̪͎͍ ͕̺͙̬̠ͅi̶ņv͏̫̣͕̠̗o͏̦k̳̰̥͖̖e̼̮̭͍̝ͅ ̰͍̹̱͇t̡͚̲͈̭̦ͅh͍̟̺̼͔͘e̝̰̰̰ ̣̯̩h̵͎͓̦̼͕̮̹i̖͇͡v͇̙͇̜̥e̙̹̺͈-͖̣͕͎m͉̣̗͚̼ì̱n̳̙̗͈d̨͉ ̪̖͜r̞̳̼͉̳͉͕ep̭͎̠r̼͖̤͚̝̝es̸̙̟e̡̬̘̦͇̺̗̫n̦͍͞t̙͚͢i̶͖̱̩͓͍̣̝n̵̟̱g̯̟ ̡c̡̦͚̼̯̜̳h͈̼̠͓̞̼͡a͚̗̫͕o͠s̼͈.̶̱̲̹͔
̞̠͍̬I͔̙͘n͉̜v̶̠̩o̢̟͈̩͓̗̻k҉̳͙̥͚̙̜͔i͘n̴ͅģ͖͇̩͎ ͈̕t͏͍̫̲͍̪̩h͕̩ͅe̶̹̼̳̲ ̤͕̟͙f̟̥̭̝̲̰̗e͙̞̦̥͚e̷͎l͇̬̼̦i̦̯̠͖n͎͖͉g̦̟̞ ̴̣̣̗͈̯͓o͔f̳̠̹̯ͅ ̬̻̩͚̭c̶̤̣̪̠̙̰̞ḥ̵̤̩̹̙ͅͅa͖o͈̻s̷̜̩͕̙̟̮.̦̪̳͍̠͙
̛͖͇̱̖̘̦̗W̳͙̮̹̯ͅit͔͘h̸̟̞̤ ͇̜̗̕o̪ṳ̥̙t̸͈͉͇̥̫̙͉ ̺̦̭̯̩̜̤͡o҉̹̮̙̥̬ͅr̞̫̖̞d͚̬̰̠͎e͎̺͙̜͔r̪̮.̝͓̜͇̮̤
̛͔̞͇T͚̞̣̯̜̱ͅh̢̯̗̲̤̞͖ẹ͔ Ņẹ̙̬̼̪̹z͚̥̪p͕e̸͖̩r̖̱͉̮̺͚d̬i̡͓̲̰͖̟͓an͓͕͙̟̤̲̘ ̨̠̬̻͖̬ͅh̗̗̩͟i͔v̢̬̙̞͍̻͇͈e̖̮̺-҉̮͇͈̗m̵i̼̩͕̻͠n̞̣̼̙̘̣ͅd̨͓̙͕ ̙̹͚͙̠̲ͅo̶̮͕̬̜͉̭f̺͎̪̻̖ ͈̥͈͇̱̫͜c̡̟̪̖͔̘h̤͚̦͡a̗̗͓̘̘̖̠os.̴̪̭͖ ̮Z̭̣̩̟̪a̝͞l͙̜̩̦͇͈͡ͅg҉͓̝̪̼̟o̢̤̹̪̺̩.̯͔͞
̡̰̝H͔͖͎͉͟ẹ̴̦̫̳ ͎̯w͇̟ḩ̗͈̯̬o̸̗ ͇̺W̺͕͡a͟i͏̙͓̦̹͕ṱ̡̰̯͕͖͕ͅs̩̭͎ ̜̳̳̦͔̀B̡͇̞̜̻̪͕̜e̤̩͓͡ͅh҉͚̯̩̯͖͓͇i͕̯͘ṇ̢̘͙̣̪̦d̳ T̡̙̼͔̩̹ẖ̲͕e͉̮̤̺͢ ̬̼̭̞̝Wa̘͓͍̻̝̠l̴͔l.̣
̦̪̥͉̝̳͘Z̠̙͇̭͠A͓̭L̶͈͙̜ͅG̱̥͟O͖̫͎̭̗͕!̡̣̤̰
я видел какой то ломал по вертикали на полтора экрана, не могу найти пример)
Кучка комбинирующих диакритиков накладываются друг на друга и в итоге в рисуются над или под символом. Немного js и готово. В итоге народ делает сервисы которые пилят ќр͠и͜по̛в̡ый текст. eeemo.net самый легкий, есть и другие, которые попутно могут еще и перевернутый / перечеркнутый / эмоджифицированный текст сделать или какой-нибудь ascii-графикой вывести.
Да, на macOS на имени файла 'уй' алгоритм тоже точно сломается. Для Unix/Linux/Windows вероятность поломки невелика есть, если только не использовать unorm, `iconv -t utf-8-mac' или иных явных средств нормализации Unicode.
Внезапно, оказывается, если в строке левая кодировка, то нужно обрабатывать эту левую кодировку.
ВОТ ЭТО ПОВОРОТ.
Да, реверс строки сломается если в строке не строка.И?

А еще меня как Сипипишника ввело в недоумение вот это утверждение:
самым оптимальным для себя я долго считал такое решение:
const strReverse = str => str.split('').reverse().join('');

split и join — это эффективно? Эффективнее чем тупо перебрать половину строки и сплитнуть руками с другой половиной? Ладно, нет возможности модифицировать строку. Но что мешает без сплита сразу писать в массив и его конвертировать в строку? Разве это не будет эффективнее?
Я бы охерел, если бы на вакансию С++ программиста кто-то для реверса строки сначала её в отдельный список/массив конвертировал.
Если в вебе это норма — не удивительно что у нас всё так плохо в браузерах.
Если я не прав в своих суждения — расскажите пожалуйста почему автор считает такой подход правильным и наиболее эффективным.

Там еще и эффективными считаются цепочки из операций filter/map над массивами, которые порождают промежуточные массивы.

Разве эти методы не работают с итераторами, как в нормальных языках?
Не работают, это filter/map/и т.д. чисто методы массивов (ну и у некоторых массивоподобных объектов есть forEach).
Если нужен filter/map для итераторов, то ищем реализацию на стороне.
Итераторы-то совсем недавно завезли, а стандартные методы были всегда.
UFO just landed and posted this here
Пожалуй я преувеличил насчет «всегда», но ES5 был еще в 2009, а ES6 только в 2015.
UFO just landed and posted this here
Вообще, насколько мне известно, трюк с
.split().reverse().join()
— это что-то вроде шутки в рамках контекста такой задачи.
Удивительно, что автор посчитал этот ответ зачетным.
ну и потом автор рассказывает что _В_ТОМ_ЯЗЫКЕ_ split() вообщем то неправильно сплитит…
Автор не использовал слово «эффективно». Автор использовал слово «оптимально» без указания критерия оптимальности. Возьмусь предположить, что имелось ввиду «оптимальное по размеру кода и очевидности».
ну так можно дойти до того, что у эффективности тоже не указаны критерии.
эффективно по количеству символов в решении? по количеству затраченного времени? по стоимости часа программиста способного это написать?
Так что ваш довод не принимается. Если заменить в моем тексте слово «эффективно» на «оптимально» суть ни на грамм не поменяется.
split и join — это эффективно? Эффективнее чем тупо перебрать половину строки и сплитнуть руками с другой половиной?


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

Автор: вот короткое решение
Вы: разве это быстрое решение?
Разве короткость — это оптимальный критерий для оценки тестового задания?
В данном случае, java script не предназначен для поиска оптимального решения задачи реверса строк по количеству необходимых операций и размера памяти, а просто демонстрирует способность кандидата использовать стандартные вещи из JS.
В питоне реверс массива выглядит еще короче, что-то типа myarray[::-1], как там «под капотом устроено» нужно смотреть дополнительно.
А вот в c++, данная задача хороший индикатор на некоторую адекватность разработчика. Ну просто не всегда нужно мыслить терминами одного инструмента при использовании другого, тем более они предназначены для разных задач.
А вам не кажется что такой подход является причиной того, что веб такое днище в плане потребления памяти и производительности?
Я часто слышу что JS гавно. Но здесь я вижу не проблему языка, а проблему выбора решения внутри языка.
что JS гавно

Смотря с какой точки зрения.
Производительность, потребление памяти, отсутствие нормальной мультипоточности (да я знаю про webworkers) по сравнению с более низкоуровневыми языками — да я тут согласен.
Но, если смотреть на порог вхождения, умение упрощать некоторые оплошности, отсутствие зубодробительных практик из того же С++ (я про указатели, наследование, темплейты, виртуальные деструкторы и т.д.), он почти идеальный кандидат для массового применения в действительно областях, где требуется большое количество разработчиков. На мой взгляд очевидно, что сайтов бизнесу можно куда больше и быстрее написать на javascript, чем на c++.
C++ в разы проще чем весь этот треш типа пизонов перлов джсов и т.д.
Что написал — то получил. Любое действие можно разобрать до минимальных шагов, а не спотыкаться. А стандартные библиотеки подняли «низкоуровневость» С++ до «среднеуровнего» благодаря своим алгоритмам и контейнерам.

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

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

Это вопрос привычки. Для того кто всю жизнь писал на С++ кажется что он проще, для того кто писал на javascript чтo javascript.
А возьмите человека который пишет на java или C#, так для него что С++, что javascript это неудобный адовый ад.
Я пишу на жабе, и вы частично неправы:
С++ для меня ад, а скрипты очень даже хороши…
удобных указателей что бы изменить внутри функции значение аргумента

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


С++ не проще. Он сложнее. Писать на нем дольше (вы и сами с этим согласны). Да, он даёт бОльший контроль за некоторыми вещами, но этот контроль часто не нужен. Вы же не контролируете из С++ переключение вентилей напрямую? Абстракции — это крайне полезное изобретение человечества.

Простые объекты не передаются по ссылке, если мы про питон. Кроме того, сделав внутри функции objectExt=objectNew вы принимаемый аргумент не измените и вообще потеряете, по сути, из виду.

Как не проще, если С++ предлагает вам почти тот же уровень абстракций, как и «простые» языки, и, если нужно позволяет вам опуститься на уровень ниже. Разве не это простота?
Для того что бы писать на «простом» языке больше чем HW, нужно все так же понимать как он внутри работает и использовать все теже фичи, что вы будете использовать и в С++. Нужно так же знать что такое ссылка, что такое указатель, без этого никуда не уедешь. Нужно понимать чем List=List+AnotherList (опять питон) будет отличаться от list.extend(AnotherList). Понимать как в зависимости от контекекста происходит каст одного типа к другому, и как будут операторы работать (JS?). Как происходит наследование, что за self. такой вообще. И многое другое.
Не нужно исключать работу с классами и объектами которые зависят от их контекста. Таже библиотека «таблиц» Pandas питоновская. Что бы понять в каком виде она приходит тебе, нужно взять все что происходит с ней до этого, причем я сейчас про структуру (колонки их типы, их названия), а не значения. В С++, если мне не нужна супер пупер гибкость на шаблонах, любой новичек сможет ткнув в структуру понять как она выглядит и какие типы содержит, и при написании кода IDE никогда не позволит ему забыть что там есть благодаря подсказкам.
В С++ все с этим проще, все работает намного более явно. А ногу отстрелить не в пример сложнее, достаточно забыть про с98 и перейти на сxx14+ с shared_ptr.

>Писать на нем дольше (вы и сами с этим согласны).
Да, и не так гибко (если вы не гуру шаблонов). Однако не сложнее точно.
Кроме того, сделав внутри функции objectExt=objectNew вы принимаемый аргумент не измените и вообще потеряете

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


если С++ предлагает вам почти тот же уровень абстракций

Почему под веб пишут на php и питоне, если С++ по вашим словам не хуже и гибче? Библиотеки? Половина их написана на си, так почему их пишут на си для питона и php, а не для самого си?


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


И они на самом деле есть. Мне абсолютно точно не нужно прямое управление памятью, когда я делаю какой-то веб-сервис. Отсутствие такого управления — это преимущество, ускоряющее разработку и облегчающее поддержку старого кода. Потому что обязательно найдется кто-то, кто захочет памятью по-управлять. И если он не сможет этого сделать, значит он не подложит мне мину замедленного действия.

Это крайне правильно. В идеальном мире объект, передаваемый в качестве аргумента, вообще нельзя поменять

Что, простите? Откуда это странное утверждение появилось?

Почему под веб пишут на php и питоне, если С++ по вашим словам не хуже и гибче?

Покажите пожалуйста где я написал что С++ гибче? Я помойму такого не говорил, а наоборот даже утверждал обратное?
А пишется потому что «простые» языки дают возможность писать гибче и быстрее.

Хочу обратить внимание, мы с вами изначально говорили не о скорости и гибкости, а о простоте. Не нужно смещать фокус, о том что быстрее будет накалякать на динамическом интерпретируемом языке я не спорю.
Откуда это странное утверждение появилось?

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


Изменение аргумента функции — это побочный эффект. Такая функция не является чистой.


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


Как пример — в JavaScript есть линтер, ESLint. При попытке изменить аргумент функции он выдает предупреждение.

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

Просьба без истерик. Если у вас есть доводы, что чистые функции "бред", буду рад их услышать. Забавно, что вы не сказали "чистые функции — плохо", вы сказали "бред". Бессмысленное, вздорное, несвязное. Вместо того, что бы загуглить "что такое чистые функции", вы сразу назвали их бессмысленным вздором. Очень забавно.


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

Эм?
У вас с чтением большие проблемы. Мы говорили о простоте, вы свели к скорости написания кода. И сейчас, опять, вы совсем другое читаете, ведь очевидно что не существование термина чистые функции — бред, термина который даже в институтах преподают, а теория о превосходстве использования только чистых функций.
И вы первый об этом написали, о том что писать на чистых функциях лучше, поэтому и флаг вам в руки в первом шаге аргументации.

Совсем другое дело. Вы просто не согласны с тем, что чистые функции — хорошо. Ни о каком "бреде" речь уже не идёт.


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


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


А поймать меня на слове "только" не получится. У нас же не идеальный мир, поэтому хаскель никогда не станет самым популярным языком.

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

Это написали вы. А теперь отправляете в гугл. Удобно.

К слову, идеальный мир, в котором нельзя менять аргумент и будет мир в котором «только» чистые функции. Так что, поймал, хехе.
ЕМНИП, в Аде были только чистые функции и «грязные» процедуры.
Так что, поймал, хехе.

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


бред как он есть?

Удачи вам с таким уровнем ведения диалога. Назовите любой тезис оппонента "бредом" и вам не придется приводить своих доводов. Очень удобно, всегда в выигрыше. Но интереса в этом нет.

В нашем мире грязные функции приемлимы, иногда их нельзя избежать.

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

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

Это поиск баланса между читаемостью и эффективностью. Если вам нужно реверснуть небольшую строку, то сплит-реверс-джоин работает так же быстро, как и что угодно ещё — там разница на уровне пары миллисекунд. При этом смысл кода понимается беглым взглядом, меньше, чем за секунду. Давайте напишем цикл:
var str = '12345';
var reversed = '';
var l = str.length;
while(l--){
 reversed += str[l];
}

— и чтобы понять, что делает этот код, понадобится секунд 5.

И так везде. Если вы пишете очень критичное место (вроде сравнения v-dom в таблице на миллион пунктов), то да, его нужно оптимизировать всеми силами и средствами, потому что какая–нибудь мелочь может привести к разницы в полсекунды. Если разницы нет, то и смысла нет — выбирайте, что читаемее.
Не работает этот так. Ну вот просто не работает. Если вы используете неэффективный алгоритм и медленно работающий код — то результатом будет новый GMail. Жрущий на порядок (если не на два) больше ресурсов, чем версия 10-15 летней давности и не дающий при этом никаких новых возможностей!
Если у вас есть массив из 4 элементов, и вы по кнопке его сортируете, то сортируйте хоть перебором, 24 перестановки комп переберёт почти так же быстро, как и отсортирует пузырьком. Место не критичное.

Медленно работающий гмейл — это результат того, что плохой код в критичном месте, понимаете? Критичные места — это то, что нужно оптимизировать.
Критичные места — это то, что нужно оптимизировать.
Если вы прочитаете соответствующую статью, то обнаружите, что… миф таки остался мифом. Судя по тому, что что там осталась куча метрик — критичные места там диагностировались и оптимизировались. Результат — тормоза и дикое потребление памяти.

Медленно работающий гмейл — это результат того, что плохой код в критичном месте, понимаете?
Нет — не понимаю. Более того — не понимаю категорически. Я ещё ни разу не видел ситуации, когда «феншуйный» год, который писали думая об «идеоматичности», «гибкости» и прочим разным баззвордам (но не об эффективности) после оптимизации работал бы хотя сравнимо по скорости с кодом, который писали изначально думая-таки об эффективности.

Если у вас есть массив из 4 элементов, и вы по кнопке его сортируете, то сортируйте хоть перебором, 24 перестановки комп переберёт почти так же быстро, как и отсортирует пузырьком. Место не критичное.
Рассуждение кажется разумным — но он в корне неверно. Ибо предполагает, что вы знаете заранее что там будет 4 элемента. А их там может оказаться, в результате работы какого-нибудь «дезигнера» и 44 и 444444 — ничего заранее предскзать нельзя, если не контролировать что вы делаете на всех этапах.

Я с этим сталкивался неоднократно: когда меня просят что-то оптимизировать и я разрушаю «весь феншуй» путём превращения кода из «торта Наполеон» в 100500 слоёв в что-то гораздо менее «феншуйное» — то у меня часто спрашивают о том, как я собираюсь это профайлить и откуда у меня будет вылезать статистика… и вы знаете… я обычно вообще не собираюсь об этом думать. Того факта, что версия «не по феншую» работает в 3-5-10 раз быстрее оптимизированной в «критических местах» «феншуйной» версии — обычно оказывается достаточно.

P.S. Это, кстати, не значит, что я совсем никогда не пользуюсь профайлером. Это было бы глупо. Но важно всегда понимать заранее — где у вас в программе неэффективности… тогда профайлер подскажет вам — какие из них реально влияют на код, а какие — нет. Если же вы списка неэффективностей не имеете — можете хоть упрофайлится, результатом будет GMail.

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

В этом случае я бы не соритировку писал бы, а пихал бы уже в отортированном порядке.
И вообще может задача найти первый отсортированный, тогда можно не доводить сортировку до конца, а сделать сортировку кучей которая после первого этапа имеет «наверху» самый большой элемент.
UFO just landed and posted this here
Конечно нет))) Сортировку кучей хорошо применять, когда не нужно сортировать все, а нужно скажем первые 10 самых маленьких/больших из большого массива данных, т.е. сортировать каждый раз полностью массив данных не требуется, в этом случае сортировка кучей хороша и это будет работать быстрее чем сортировать каждый раз той же быстрой соритровкой.
Если смысл именно найти наибольший, то естественно нет смысла. Наиболее разумно либо класть уже в отсортированном порядке либо класть в конец и вызывать сортировку кучей, если предваритель но массив уже был кучей, то установка нового элемента в нужное место кучи будет быстрой. короче это хороший способ если периодически требуется выдавать несколько самых больших/маленьких элементов, переодически вставляя значения.
На входе — несортированный список интерфейсов, прилетающий из другой либы. Надо выводить отсортированный список, плюс требуется последовательно пройтись по интерфейсам, найдя первый, удовлетворяющий определенным критериям.
В изначальном вопросе не было сообщения, что массив прилетает из вне.

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

Можно в принципе передалать архитектуру, чтобы при подключении/отключени устройства к нам прилетало это событие и мы внутри себя уже хранили бы отсортированный списк, тогда сортировку проводить каждый раз было бы не надо и надо было бы выбрать по критерию и вернуть уже хранимый нами отсортированный список.
Почему нет? Правильно написанный квиксорт для небольших размеров подмассивов использует сортировку вставками.
Как результат у нас будут эффективно сортироваться как маленькие массивы, так и большие.
Тогда это уже не квиксорт, а какой то гибридный вариант сортировки. И да, он будет больше и сложнее каждого из этих алгоритмов. Я уж не говорю о том, что для однократной сортировки очень маленьких размеров данных вообще пофиг какой алгоритм выбрать.
Почти во всех библиотеках он именно так и реализован для того, чтобы убыстрить его. Вызывая его совершенно не важно, что он гибридный. Это уже давно так. Нужен чистый — пиши свой, но зачем? Мне, например, бетчера нравится из-за того, что его отлично можно распаралелить.
Зачем тратить время на реализацию квиксорта для десятка записей, которые сортируются один раз при втыкании устройства USB? Какой в этом смысл? Сделать определение устройства не 15 секунд, а 14.999999?

А зачем его тратить? Квиксорт есть в стандартной либе многих языков. В той же жабе как раз таки со встроенной сортировкой вставками. Вот если его нету, тогда другой вопрос. Хотя опять же, простой вариант квиксорта пишется примерно за столько же, за сколько и сортировка вставками/выбором/пузырьком. Может на 5 минут дольше. И это если лень залезть в гугл и найти имплементацию например того же Седжвика.
Всё это очень здорово, но представте что «не феншуйно, но зато быстро» пишете не Вы, а вчерашний студент, который старается так, как может. Потом пару лет этот код поддерживает команда из нескольких человек с разным уровнем навыков — джависты, эс-ку-эльщики, заскучавший скрам-мастер, который решил что ему между многочисленными митингами неплохо бы и в программировании поупражняться… При этом на команду давят со сроками, поэтому обновлять документацию времени нет, а юнит-тесты поддерживаются лишь так, чтоб те своими падениями не мешали бы деплою.

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

Начну с того, что у моего руководства есть идея фикс: «сейчас мы наймем новых людей и они сделают все хорошо». Совершенно восхитительная идея, что набивший шишки на предыдущих проектах, опытный работник заведомо хуже любого новичка. Я сам, кстати, так пришел, и был в восторге от возможности разрабатывать архитектуру, делать все так, как хочется, без оглядки на кого бы то ни было. У меня тогда хотя бы лет пять опыта разработки было.

Пару лет назад началась очередная итерация «сделаем все по-новому». Пришли студенты (прям реально студенты, они тогда еще доучивались) и понеслось. Нет, ребята очень умные, вот только опыта никакого. Запилили структуру данных, в которой и десятка тысячи записей никогда не будет. Ни о какой высокой нагрузке речи не идет — по сути это конфиг устройства, обращения к которому достаточно редки. Умудрились впихнуть туда самописные би-деревья и хэш-таблицы. И это не смотря на то, что сам конфиг хранится в JSON, а используемая либа — jansson — сама по себе (сюрприз!) основана на хэш-таблицах. Нет, надо показать, что мы не лыком шиты и знаем алгоритмы.
И такое везде — хитрые операции с указателями, огромные макросы, трюки с gcc. Такие практики имеют право на жизнь, но там, где они необходимы, а не по всему коду, который становится абсолютно нечитаемым.
Самое смешное обнаружилось, когда эта разработка начала внедряться, и с ней пришлось работать остальным. Ребята не сделали НИЧЕГО для синхронизации доступа из разных процессов! После того, как на это им указали, сначала вяло отнекивались, что это должно работать как-то «само по себе», а потом все-таки запилили один big fucking lock на весь конфиг. На предложение сделать ниспадающую блокировку, замораживающую только ветки, растущие из нужной ноды, сказали, что это сложно и долго делать. Вот так: оптимизировали би-деревьями, а потом все процессы ждут, пока один закончит свою работу.

Что имеем в результате. Косую архитектуру, никакую мотивацию у старых работников и постоянное переписывание кода. Зато за это время мы слышали много умных слов про «чистый код» (в реальности весьма кривой), про крутость датамодели (в реальности очень костыльная реализация в JSON) и про оптимизации (в реальности хэш-таблицы на списке из ТРЕХ команд).

Похоже, вы разбираете говнокод посредственных программистов. Это нормально, потому что посредственных больше, чем хороших. Но вы ради интереса разберите хороший проект на javascript. Vue.js, например (можете выбрать любой, какой хотите). Ускорить, удалив 80% возможностей вы, без сомнения, сможете. Но сможете ли ускорить, оставив функционал?

Ускорить, удалив 80% возможностей вы, без сомнения, сможете. Но сможете ли ускорить, оставив функционал?
Смотря что понимать под «функционалом». Если мы о бизнес-требованиях — то да, легко. А вот если о метаниях дезигнеров или всякого рода баззвордом — нет, конечно. И не нужно.

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


А люди, которые делают 20 слоев абстракций — сделают. Что ж, это полностью объясняет, почему у софта, который всё же взяли и сделали, так много слоев, не правда ли?


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

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


Я думаю, там вопрос не столько в качестве кода, сколько в количестве. Не даром он там 600мб памяти занимает.
Почему гмайл не даёт новых возможностей? Новые возможности у товарища Майора и Гугла для впихивания рекламы

Он даёт очень много возможностей по сравнению, например, с аутлуком. Не знаю, на чем написан аутлук, но точно не на JavaScript.


Только не спрашивайте, каких. Просто посмотрите, сколько людей пользуются Gmail. Значит у него есть какое-то преимущество перед аутлук. Преимущество именно для них.

UFO just landed and posted this here

Я про тот, который вообще не в браузере.

Эмм. Очевидное преимущество gmail'а перед outlook'ом — он бесплатный (ну, на поверхности). В плане фич и скорости работы gmail'у до него далеко, но я не вижу причин вообще сравнивать бесплатное веб-приложение и дорогой офисный продукт.
Плюс многие пользуются gmail'ом именно как почтовой службой, а его сверхтяжелый интерфейс им даром не нужен. И, сюрприз, есть люди, которые читают почту своего gmail-ящика через outlook.

Кому-то не нужен. Кому-то нужен. Вы правильно описали, что у разных людей разные причины пользоваться. А утверждение выше, что "gmail" не даёт новых возможностей, не верно. Раз пользуются, значит им даёт. Благо выбор огромен.

Раз пользуются, значит им даёт.
Пользуются потому что и раньше пользовались.

А утверждение выше, что «gmail» не даёт новых возможностей, не верно.
Можете назвать хоть одну? Ещё раз: я не про GMail 2004го года (там как раз требований к ресурсам было не так много, а новых фич — много), а про GMail 2018го из анекдота «зачем ты Ролекс за миллион купил, вот там за углом такой же за пять».

Могу, конечно. Группировка писем по категориям/полезности. Вы скажете, что это можно было реализовать и в старом gmail? В теории — да, можно. На практике разработчики быстрого кода сказали, что "это не нужно" и пришлось gmail перепилить силами любителей абстракций. А что делать? Отказываться от реализации фичи, потому что разработчик опять говорит "не нужно"? Это хуже, чем медленный софт, приходится мириться с тормозами.


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

Группировка писем по категориям/полезности.
Вот только эта группировака уже была в старом GMail. Она не в 2018м году появилась.

Просто представьте себя на месте владельца софта.
Представляю. Пользователи уже начали уходить в IMAP и прочее. А со временем — могут и к другим провайдерам уйти.

Видимо я не смог нормально описать, что я имею ввиду. Та группировка, о которой я веду речь, появилась года два назад.


Впрочем, не существенно. У вас, видимо, есть другое объяснение, почему gmail сделан так. Если я вас верно понял, это потому что там одни идиоты и любители специально делать плохо. Что-то я сомневаюсь, что это на самом деле так, но дело ваше.

Та группировка, о которой я веду речь, появилась года два назад.
А я говорю о новом интерфейсе, появившемся в прошлом году.

Если я вас верно понял, это потому что там одни идиоты и любители специально делать плохо.
Ни в коем случае. Этот случай уже разбирали. Просто за 15 лет с момента выхода первой версии GMail изменилась культура внутри компании.

15 лет назад, когда выходила первая версия GMail, её разрабатывали и оценивали инженеры. Сегодня — это делают, как и везде, «эффективные манагеры». Которые ценят «запуски» (launch) и «влияние» (impact). Которые, как известно, сводятся к игре шрифтами (ну и можно ещё баззвардов подсыпать).

Ну а дальше — имеем то, что имеем. Никто не будет писать хороший код, если куда важнее — создать хорошую презентацию и «документ о дизайне» (design doc). То, что в результате получится дерьмо, которое будет жрать ресурсы как не в себя и тормозить (см. GMail) — никого не волнует. За то, что будет порождено 100500 продуктов, ни одним из которых нельзя будет пользоваться (Hangouts, Allo, Duo, какой-там-ещё был высер, якобы конкурирующий с WhatsApp?) — никто «втыка» не получит.

В резльтате — я реально не знаю ни одного пользовательского продукта, сделанного в Google в последние лет 5-7 и получившего популярность. Backend? Всякие TensorFlow? Да — их делают «придурки», не умеющие ублажать «эффективных манагеров» и не получающие много плюшек, но и зато не имеюшие уж очень много манагеров на шее.

Frontend? От плохого к худшему, уж извините.

P.S. Это, впрочем, нормально: Microsoft тоже через этот этап прошёл. Там, чтобы выправить ситуацию пришлось CEO выпереть… посмотрим что в Google потребуется…
Инжинеры в гмыле себе немного оставили mail.google.com/mail/u/0/h/1pq68r75kzvdr/?v%3Dlui
Сначала пользовались ей иногда, теперь постоянно, т.к. стандартная версия даже на десктопе уже достала тормозить.

Тема от подходов к программированию плавно перешла к управлению продуктами. Tensorflow на самом деле хорош. Облачные продукты у них нормальные. Интерфейс, конечно, шлак и документация постоянно устаревшая, но богатство возможностей поражает. Андроид не плох. Конечно он родился больше чем "5-7 лет назад", но новые версии вполне вменяемые.


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

Он даёт очень много возможностей по сравнению, например, с аутлуком.
Я, вообще-то сказал достаточно однозначно: «не даёт ничего по сравнению с тем же GMail'ом десятилетней давности». Когда уже и чаты и всякие прочие плюшки (даже тот самый Google Buzz) уже были — а таких объемов… добра в кода и таких тормозов — не было и в помине.

И я даже знаю какие вещи (в том числе полезные) за это время появились (скажем Priority Inbox) — но я не знаю ни одной из ничего, что нельзя было реализовать в версии той же десятилетней давности, требующей на порядок меньше ресурсов и на порядок более отзывчивой…
Да, но если вы этот цикл запихнете в функцию reverseString, то чтение вызова оной также займет мгновение. В сплите-джойне вы точно так же кучи циклов прячете за фасадом библиотеки.
Я то же самое про РНР постоянно слышу. А проблема та же)
Как я ниже уже сказал, вопросы к языку тоже есть. Например, почему map и filter аллоцируют объекты.
Как выяснилось, если применять стандартные вещи без понимания, тестирования, тяп-ляп и коротко — попадёшь на грабли. Автор начал писать за это, но тут же попал на следующие грабли, с составными символами. А, скажем, на macOS, iOS, стандартная нормализация NFD.
Если под «оптимальностью» имелось ввиду «оптимальное по размеру кода и очевидности», то зачем так извращаться? Можно ведь просто использовать for…
function func(str) {
	ret = "";
	for(i = str.length; i >= 0; i--)
		ret += str.substr(i, 1);
	return ret;
}
Разве каждую итерацию не будет повторное выделение памяти?
По идее здесь нужен билдер, который будет принимать сразу размер и потом заполнять не меняя размер. но я не знаю как это в JS делается.
Я тоже не уверен, что с этим будет делать JS. Скорее всего этот способ займет много памяти. Но по своей простоте и очевидности, способ по-моему самый простой. Ведь что конкретно автор имеет ввиду под «оптимально» мы пока не выяснили…
Можно. Но, имхо, однострочник проще и понятнее.

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

Вот кстати бенчмарк с разными способами: jsperf.com/string-reverse-function-performance
Ага, особенно учитывая что самый быстрый вариант выглядит вот так:
// only for-loop declaration with concatenation 
function reverse_06 (s) {
  for (var i = s.length - 1, o = ''; i >= 0; o += s[i--]) { }
  return o;
}
Да, на моей машине jsperf показывает для этого варианта 2.6 миллионов операций в секунду, а для однострочника — всего 1.6 миллионов операций в секунду.

Поэтому если вдруг (хз правда зачем) я пишу микросервис на js, который должен реверсить строки в промышленных масштабах, то я возьму более быстрый вариант. В прочих случаях я предпочту более компактный и понятный, потому что даже с ним операция занимает меньше 1 микросекунды.
А почему не вот так?
function reverse_06 (s) {
  for (var i = s.length, o = ''; i--; o += s[i]) { }
  return o;
}
Почему люди (особенно с сишным бекграундом) так стараюстя уместить максимальное количество сайдэффектов на единицу площади? Если расписать функцию нормально строк в 5 она будет читаться сильно проще, чем этот остроумный вариант использующий все дополнительные свойства операций.
Этим страдают джуны, которые хотят показать таким образом своё мастерство в знании ЯП.
Этим страдают те, кому нечем заняться, и хочется слегка пошутить на тему кода.
В проект это совать… Комментов больше чем кода напишешь.
Комментов больше чем кода напишешь.
Там не может быть ни одного коммента. Ну никак. Весь кайф он наблюдейния глаз человека узревшего чего-нибудь типа if any(iter) and not any(iter): (это, правда Python) потеряется…
UFO just landed and posted this here
UFO just landed and posted this here
Почему не
function reverse_06 (s) {
  var o = '';
  for (var i = s.length; i >= 0; i--) {
     o += s[i];
  };
  return o;
}

Тогда уже.
Хотя почти это же написали выше.
Ну вот, вы предложили решение с квадратичной сложностью. NO HIRE!
UFO just landed and posted this here
Строго говоря «эффективно» и «оптимально» это две формы одного и того же понятия.
Потому что эффективность так же требует критерия как и оптимальность. Реализация может быть эффективна по: тактам процессора, используемой памяти, простоте дальнейшего сопровождения, эффекту производимому на вопрошающего и т.д. и т.п. И вовсе не факт что это будет одна и та же реализация.
У браузеров сейчас довольно продвинутые JIT-компиляторы. Я недавно офигел, когда реализовал force-directed алгоритм рисования графа двумя способами:

  • Максимально разжеванным для компилятора с циклами вида for (let i=arr.length-1; i>=0; --i), заранее выделяя все массивы нужной длины, с выносом всего неизменного в константы, явным приведением типов в духе asm.js и хранением всех данных в плоских типизированных массивах.
  • Модном сейчас функциональном стиле с кучей лямбд, map, созданием десятка промежуточных массивов, хранением данных во вложенных словарях с обращением по строковому идентификатору.

и оказалось, что время их выполнения абсолютно одинаково и примерно равно времени выполнения такого же кода на C.

Относительно кода автора, уже были сделаны бенчмарки, сравнивающие время выполнения обращения строк и выяснилось, что разница примерно в 2 раза по сравнению с обычными циклами: jsperf.com/string-reverse-function-performance (код автора: «in-built functions»). Да, не оптимально, но в рамках допустимого: если это не узкое место в программе, то решение в одну строчку гораздо более читабельное и поддерживаемое. Думаю, если бы не умные оптимизации компилятора, разница была бы раз в десять.
А я вот в своё время поржал, что замена const на немодный var может ускорить некий синтетический код раза так в два.
у меня бывало наоборот — числовой const начал инлайниться по месту употребления, но это происходило нерегулярно и без гарантии. Делал это встроенный минификатор Webpack
и оказалось, что время их выполнения абсолютно одинаково и примерно равно времени выполнения такого же кода на C.

Надо думать второй вариант будет потреблять больше памяти и чаще дергать сборщик мусора, т.к. функциональный стиль подразумевает иммутабельность aka «создание десятка промежуточных массивов»
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
«создание десятка промежуточных массивов»

Не обязательно, умные компиляторы/интерпретаторы умеют создавать только изменения или вообще менять in-place при сохранении иммутабельности с точки зрения программиста, т.е. когда это можно делать и выгодно делать.

я добавлю, что на том бенчмарке тестировалась строка длиной в 61 символ

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

Смысл в том, что перформанс надо тестировать в конкретных условиях
Мир фронтенда — это апофеоз неэффективности. Само собой, когда туда идут только бракованные программисты.
Я бы охерел, если бы на вакансию С++ программиста кто-то для реверса строки сначала её в отдельный список/массив конвертировал.

А C++ что ли не умеет в итераторы?

Здравствуйте. Перформанс этого метода конкретно в JS неоднозначен и требует тестирования в конкретных боевых условиях. Еще в 2009 году были проведены тесты, в которых было показано, что если строка превышает 64 символа в длину — метод str.split('').reverse().join(''); внезапно начинает обгонять метод с циклом. Я не говорю — этот тест актуален и сейчас. Я говорю — надо тестировать перформанс в конкретных боевых условиях.

shamasis.net/2009/09/javascript-string-reversing-algorithm-performance

На всякий случай, если ссылка сдохнет.

// быстрее на строках > 64 символов
String.prototype.reverse = function() {
    return this.split('').reverse().join('');
};

// быстрее на строках < 64 символов
String.prototype.reverse = function() {
    var i, s='';
    for(i = this.length; i >= 0; i--) {
        s+= this.charAt(i);
    }
    return s;
};

Первый вариант имеет сложность O(n), а второй вариант — O(n^2), так как на каждой итерации происходит конкатенация строк. Правильно было бы сравнивать вариант с цепочкой вызовов с этим:


String.prototype.reverse = function() {
    var i, a= [];
    for(i = this.length; i >= 0; i--) {
        a.push(this.charAt(i));
    }
    return a.join('');
};

Этот вариант так же имеет сложность O(n), но потребляет меньше памяти (с точностью до коэффициента, так как потребление памяти у них одинаковое — O(n)).

Хм, интересно, не знал. Спасибо за информацию.
Пожалуйста. Я сам постоянно страдаю от желания оптимизировать все эти цепочки, и если копаться в моем коде — наверняка можно найти кучу вещей, которые можно было бы записать короче, а не циклами. Мне до сих пор непривычно, что конкатенация строк плюсом самая быстрая в JS.

Но, как я читал, в Java идут разговоры о том, чтобы в следующих версиях улучшить реализацию String так, чтобы конкатенация по дефолту происходила так же быстро, как сейчас через StringBuilder. Так что, вполне вероятно, лет через 10 мы все забудем про конкатенацию строк как дополнительный n )

Я часто работаю со строками (пишу мини-парсеры для сменного контента в мини-играх), поэтому постоянно ищу, где бы улучшить обработку строк — но пока прихожу к выводу, что оптимизацию надо делать отдельной задачей, после того, как реализован проект — и там уже менять то, что дает заметный для пользователя эффект тормозов
Круто. В 2011 было наоборот, из-за этого у нас всюду, где код для старых браузеров, join массива.
Похоже что даже в разных версиях SpiderMonkey, оптимизации немного разные. Впрочем, разница невелика. А вот V8 действительно сильно оптимизирован на конкатенацию.
Лиса
image

Хром
image

Хотелось бы ещё увидеть результат с инициализацией массива через new Array(10000). Без него как-то не интересно.
Как и ожидалось, предварительно выделенный массив работает быстрее, чем обычный. Но оптимизированная конкатенация, там где она есть, всё равно даёт лучшие результаты.
Лиса
image

Хром
image

Ну и для полноты картины:
Edge
image

IE
image

Опера 12
image

Ссылка на модифицированный тест

Ага, а реаллокация массива в таком варианте, значит, не происходит?


Тогда уж лучше так?


String.prototype.reverse = function() {
    var i, a= this.slice(0);
    for(i = this.length; i >= 0; i--) {
        a[this.length - i] = this[i];
    }
    return a;
};

Да, точно, совсем забыл. Можно проинициализировать еще так:


new Array(this.length)
UFO just landed and posted this here

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

Если это UTF строка, то по логике, взятие символа b=a[i] или замена a[i]=c, должно обрабатываться корректно, в не зависимости из скольких байт состоит, т.е. с учетом стандарта UTF и этих его эмодзи, не к ночи будут упомянуты.
Это UTF-16 строка. Там суррогатные пары попадаются.

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

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

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

Есть проблема в том, чтобы юникод читать конечным автоматом с конца строки

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

Хм, ну тут я показал свое незнание. Но есть ещё вопрос, сколько по времени займёт поменять местами символ и суррогатную пару?

Ну, мы же не в исходной строке меняем, а формируем новую строку.
Вот, мне было нечем заняться, и я написал генератор, перечисляющий кодовые точки в обратном порядке
function* pointsFromEnd(str){
  let i = str.length, s="";
  for(;i--;){
    let a = str[i];
    if(s){
      yield a+s;
      s="";
    }
    else if(a>="\uDC00" && a<="\uDFFF"){
      s=a;
    }
    else{
      yield a;
    }
  }
}

А в чем разница тогда? Экономия трети памяти, но более медленный и костыльный код в котором для длинных строк могут быть миллионы вызовов этой функции.
А чтобы сделать обработку эмодзи из поста код придётся вообще целиком выкинуть.

А в чем разница тогда?

По сравнению с чем?

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

Да, и мы удивляемся, почему сайты стали такие медленые :)
split и join — это эффективно?

Зависит от реализации.

Но что мешает без сплита сразу писать в массив и его конвертировать в строку? Разве это не будет эффективнее?

если reverse() дает итератор, а не массив, то будет так же эффективно. Другое дело, что в ЖС нет концепции итераторов, и каждый filter/map/..., которые можно было бы выполнять по цепочке каждый раз аллоцируют объекты.

А вообще в ЖС есть намного более печальные способы убить производительность, чем сделать одно лишнее копирование. Держать представление всех объектов в 3 разных местах, например.
Их все еще нет, пока 99% проектов транспилятся в ES5.
UFO just landed and posted this here
Объект-итератор скорее всего будет дороже, чем просто скопировать массив.

Смысл итераторов в том, что это по идее zero cost, который развернется оптимальным образом.
UFO just landed and posted this here
и? Это никак не опровергает того факта, что работа с итератором может быть чаще дороже чем с массивом.

Смысл итератора в том, чтобы это была абстракция времени компиляции, которая полностью убирается в рантайме.
UFO just landed and posted this here
Только те, кому нужна поддержка IE или Opera Mini (одному проценту проектов). Все остальные браузеры уже давно поддеживают.
Ну давайте проведем опрос. Я уверен, что ES5 таргет основной с большим отрывом.
Я тоже думаю ES5 основной target. А ещё можно модно написать for… of из ES6 для итерации по массиву и последний Хром будет работать почти так же быстро, как и просто проход по массиву. А вот в Firefox заметно медленнее. Поэтому транспиляция в ES5 даст не только совместимость, но ещё и лучше скорость в некоторых случаях.
А я уверен, что es2017, ибо отлаживать пониженные async-await — то ещё развлечение.
Я сперва вашей подожду.
Если в вебе это норма — не удивительно что у нас всё так плохо в браузерах.
Это близко к норме в вебе.
Если посмотреть на пресловутый «leftpad, ломавший интернет своим отсутствием», там тоже был очень неэффективный (а в некоторых _редких_ случаях было бы совсем плохо) код.
(Добавление по одном пробелу слева вместо того, чтобы сразу добавить нужное количество.
Да, это экономит усилия программиста и помогает избежать "третьей из двух проблем программирования". )
Как только речь заходит об эмодзи и их сочетаниях, в памяти должны всплывать такие слова, как code point, сурогатная пара, графема. Тысячи статей про отличия букв, байт, нормализацию юникода и прочее. Кстати, при простом гуглении разделения по графемам в js находится, например: github.com/orling/grapheme-splitter
Это всё к тому, что надо знать разные тонкости, а так же иметь банальный IT кругозор чтобы знать, что спросить у гугла. В этом смысле задачка отличная.

Абсолютно с вами согласен. Спасибо за ссылку.

<ворчание-бурчание>Вот когда вводили юникод, флагами махали, нам нужны «мультиязычные сайты»… ну и где эти миллионы мультиязычных сайтов? Зато куча места на stackoverflow посвящено вопросам «как мне из utf-8 сделать что то однобайтовое», или «помогите! у меня кракозябры в консольке!»… а теперь вот эмодзи эти ваши </ворчание-бурчание> :)
где эти миллионы мультиязычных сайтов?
В китае и японии:)
Мы до сих пор активно используем однобайтовые кодировки, если объемы текста для обработки большие и язык в однобайтовую помещается. Мало того что php до сих пор не вполне адекватно работает с утф8 (за другие языки не особо знаем, но наверняка нюансы есть), так еще и на скорости и объемах отражается. Небольшое количество альтернативных символов в тексте вполне решается через &что-нибудь там.
Кстати да. Из за двубайтных кодировок сжигается больше электроэнергии при хранении и передаче и портится природа… :)
PS: мы тоже на однобайтовой сидим и чесно говоря не вижу проблем. 2 языка анг и рус легко поддерживаются, а больше у нас нет и не будет :)
Небольшое количество альтернативных символов в тексте вполне решается через &что-нибудь там.
Из за двубайтных кодировок сжигается больше электроэнергии при хранении и передаче и портится природа… :)
А сколько энергии сожглость при передаче русских букв в виде "&eacute;&ocirc;" (это только две буквы) и т.д.? ¯\_(ツ)_/¯
Когда в системе десятки баз, суммарно на десятки же терабайт, разница между двухбайтовым и однобайтовым хранением строк начинает приобретать ощутимый размер.
К примеру, в организации, где я работаю, было принято именно такое решение. Использовать в базе однобайтные кодировки.
Одной латиницы с диакритикой хватает.
Твиттер, например: я часто там вижу никнеймы с арабскими, китайскими символами, не говоря о русских. Все это прекрасно отображается благодаря Юникоду. Не хочу в убогое 8-битное прошлое с сбивающимися кодировками и невозможностью отображать рядом символы разных языков.
невозможностью отображать рядом символы разных языков.
Распространенное заблуждение у тех, кто не застал до-утфную эпоху:)
Неудобство — да, невозможность — нет. Ибо html entities

Это кривой и неработающий костыль. Простой пример: вам надо посчитать длину строки. Как вы это сделаете? Напишите свою реализацию strlen с поддержкой entities? Другой пример: вам надо отрезать последние 5 символов. А если вам надо сделать поиск регуляркой? Третий пример: а как пользователь введет свое имя, если у вас страница в 8-битной кодировке, а оно у него с арабскими символами? Их нет в вашей кодировке и браузер не может их отправить (кодирование форм с помощью HTML entities не предусмотрено).


Далее, когда вы захотите вставить в одну строку другую, вам надо помнить о том, какие данные в этих строках — сконвертированные в entities или нет, и сконвертировать при необходимости, чтобы например символ & был бы закодирован как & amp ;. и так у вас в программе будет половина строк закодированных. а половина нет, и вы будете делать ошибки или писать типы-обертки для разных видов строк. Плюс, если вы выведете где-то незакодированную строку, возникнет XSS-уязвимость. А понять, есть она у вас или нет, нельзя, так как данные экранируются в самых разных местах кода.


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

Простой пример: вам надо посчитать длину строки. Как вы это сделаете? Напишите свою реализацию strlen с поддержкой entities?

Подозреваю, что strlen тоже неочень дружит с составными emoji.

С вводом утф, одни неудобства, заменили на другие. Плюс траффик и занимаемое место выросли в среднем от 20 до 30%
Простой пример: вам надо посчитать длину строки. Как вы это сделаете?
Наш оппонент писал про "невозможность отображать рядом символы разных языков",.

Простой пример: вам надо посчитать длину строки. Как вы это сделаете? Напишите свою реализацию strlen с поддержкой entities? Другой пример: вам надо отрезать последние 5 символов. А если вам надо сделать поиск регуляркой? Третий пример: а как пользователь введет свое имя, если у вас страница в 8-битной кодировке, а оно у него с арабскими символами? Их нет в вашей кодировке и браузер не может их отправить (кодирование форм с помощью HTML entities не предусмотрено).
И чем пример с длиной или отрезанием отличается mb_strlen, mb_substr? Которые как раз свои реализации strlen для утф8? А необходимость использовать ключ u для регулярок с утф8 Вы забыли? Если этого шаманства специально для утф не делать, то ничего работать и не будет.

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

В общем, число граблей тут такое, что разработчики либо будут значительное время тратить на их решение, либо проигнорируют и будут писать кривой код с кучей уязвимостей. Освоить Unicode все же будет выгоднее.
Поддержка широкого набора символов должна идти идет на уровне языка. Для разработчика на языке нет разницы между mb_substr или html_entites_substr или substr. Для разработчика языка сложность реализации утф8 и хтмл_ентитес примерно на одном уровне, хотя и разного качества — хтмл_ентитес чем-то проще, т.к. каждый атипичный символ начинается предсказуемым образом, а в утф8 даже длину символа не знаешь пока не прочитаешь его. Это уже не говоря о зоопарке utf кодировок — utf8, utf16 и т.д…
хтмл_ентитес чем-то проще, т.к. каждый атипичный символ начинается предсказуемым образом, а в утф8 даже длину символа не знаешь пока не прочитаешь его.

Почему? Чтобы узнать длину символа UTF-8 достаточно прочитать его первый байт.
Это если по дороге BOM не потерялся, что случается не так уж и редко кстати. И если есть шанс что придет поток с UTF-16 или UTF-32 контентом, то уже надо несколько больше телодвижений сделать.
UTF-8 не нуждается в BOM.
У меня нет ни одного файла с BOM, и у нас по стандарту проекта «UTF-8 без BOM».
Если работать с «зоопарком» внешних систем, данные будут приходить как с ним, так и без него. И на уровне http будут ситуации, когда в заголовке одна кодировка, а внутри другая. Никто не спорит, что все можно решить, но существенная часть этих решений, тратит свое время не на бизнес-логику, а на различные корректировки и приведение к спецификации.
Емнип, надо прочитать все, кроме последнего.
Вообще-то нет. Количество единиц в старших разрядах первого байта однозначно определяют длину символа.
«0XXXXXXX» — однобайтовый символ;
«110XXXXX» — двухбайтовый;
«1110XXXX» — трёхбайтовый;
«11110XXX» — четырёхбайтовый.
Все последующие байты символа имеют вид «10ХХХХХХ».
Почему? Чтобы узнать длину символа UTF-8 достаточно прочитать его первый байт.
Сорри, не то имели ввиду. Имели ввиду, что даже если у Вас текст полностью в одной из однобайтовых кодировок, то Вы точно знаете что 1 это 1, а вот в утф8 это не так однозначно. Таким образом утф8 непредсказуем с самого начала, а однобайтовые кодировки непредсказуемы только когда есть атипичные символы.
однобайтовые кодировки непредсказуемы только когда есть атипичные символы.

UTF-8 тоже. Просто «атипичными символами» считаются все, после 127-го.
UTF-16 — ещё лучше — «атипичные» только суррогатные пары.
UTF-8 «атипичными символами» считаются все, после 127-го.
Это и есть проблема. Утф8 считает атипичными символами вполне типичные символы типичного сайта, типично сделанного на одном типичном языке. Из-за чего даже банальный природно-однобайтовый сайт даже тупо на немецком/русском/французском вдруг оказывается с точки зрения утф8 атипичным.
Это не проблема, если вспомнить, что большую часть даннх составляёт HTML/CSS/JS, а не собственно текст на человеческом языке.

А учитывая, что «атипичные» символы что в UTF-8, что в UTF-16 занимают по 2 байта, то никакой проблемы здесь нет.
Это не проблема, если вспомнить, что большую часть даннх составляёт HTML/CSS/JS, а не собственно текст на человеческом языке.
В БД все же текст, и именно с текстом проводятся все операции — поиск, обрезание, редактирование, анализ.
Но даже если говорить не про хранение, а про распространение — то разница все равно есть и к тому же хотелось бы надеятся, что html не занимает бОльшую часть данных.

«атипичные» символы что в UTF-8, что в UTF-16 занимают по 2 байта, то никакой проблемы здесь нет.
Так речь шла о сравнении однобайтовой национальной с юникодом.
немецком/русском/французском
Мне больше интересно куда вы свой немецко-русско-французский текст с однобайтовой кодировкой собрались засовывать…
Неудобство — да, невозможность — нет. Ибо html entities
Я в прошлом году занимался интеграцией нашей компании с одним крупным европейским производителем (2 место в мире по производству, годовой оборот 20млрд долларов).
Они используют в своем api для текстовых сущностей не UTF, а html entities

Так даже сам stackoverflow мультиязычный.

Ну миллиона не наберется :) по факту это соцсети (и то не все) и популярные хабы типа stackoverflow
Я не понимаю людей, которые говорят: «Я не понимаю, зачем нужно знать, что null >= 0? Мне это не пригодится!». Пригодится, 100% пригодится, в тот момент, когда ты будешь выяснять причину того-или иного явления — ты прокачаешь себя, как программиста и станешь лучше.

Потому что махровое легаси, от которо всех тошнит, но починить WTF JS обойдется слишком дорого?


Вы, как тимлид и все такое, можете объяснить логику за этой табличкой? https://getify.github.io/coercions-grid/

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

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

Я вот честно скажу, что 5 лет писал на C#, пока понадобилось заглянуть в ECMA334/335, и то для того чтобы свой интерпретатор написать. А в жсе получается два объекта сложить нельзя чтобы не пришлось знать все нюансы? Такое себе.

оказывается в стандарте юникода есть вариант объединения не только emoji, но и просто символов… Но это — совсем другая история.

Как по мне, так именно эта история. Обычно юникод перед анализом нормализуют: либо разбивают все разделяемые символы на спец последовательности, либо наоборот собирают все последовательности в соответствующие символы.

Далеко не для всех последовательностей есть собранные символы. Большинство из тех что есть, существуют больше по историческим причинам.
Почему-то я сразу споткнулся вот об это:
Если с теорией все просто — мой любимый вопрос это: «чему равен typeof null?», по ответу сразу можно понять, кто сидит перед тобой, джун — просто правильно ответит, а претендент на сеньера, еще и объяснит почему.

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

Я не знаю кто я. Джун, мидл или кто-то еще, но если меня просят сказать, что будет в результате операции, то я и скажу результат.
Если попросят результат и объяснить почему — они это и получат.
Без ТЗ — результат ХЗ

Простите, я вас, вероятно, неправильно понял. Вы на собеседованиях отказываетесь отвечать на теоретические вопросы и решать небольшие задачки?


Многие не знают, кто они: "Джун, мидл или кто-то еще", это нормально, границы очень условны и размыты, однако в вакансии в 90% случаев указано Junior/Middle/Senior, а значит кандидат, если приходит собеседование сопоставляет свой уровень с указанным.

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

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

Когда ко мне приходит менеджер какого-нибудь отдела, и просит написать решение для их проблемы, я слушаю что он от меня хочет, но делаю то, с чем ему будет удобнее и быстрее работать. За последние 5 лет все всегда оставались довольны.
Хотя, разумеется, это зависит от позиции, на которую ты идёшь.

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

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

Это всегда важно, но внезапно, не на техническом интервью.


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


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


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

Как по-мне, то не руководитель некомпетентен, а он лишь даёт пример того задания, которое ты можешь услышать у себя на работе. И смотрят на твою реакцию.
Это как дать слесарю задание на собеседовании что-нибудь выпилить на станке, а он должен ответить, что он не может это сделать сейчас, так как у него нет защитных очков, например.
Всё-таки цель собеседования — проверить, подходит ли человек для работы, на которую он собеседуется, а не просто поставить абстрактную оценку его умениям.
Если бы я спросил людей, чего они хотят, они бы попросили лошадь побыстрее.

И чем ему не нравилось средство передвижения со встроенным автопилотом, которое заправлялось само от экологически чистой солнечной энергии (посредством травы)?..

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


А вообще, вспоминаем "Условие как компромисс".

Тоже странно. Мне почему-то казалось, что сеньор от джуна отличается скоростью и простой (эффективностью само собой) решения задачи, а не полнотой академических знаний. А то ведь получается, как в школе у плохой училки: своими словами не надо, рассказывай слово в слово, как написано в учебнике
Окей, эмодзи это по-сути красивости и их объединения это уже крайности. А как вам задача инвертировать строку с текстом арабицей?
А в чём проблема? Алфавит как алфавит.
Не, там составные буквы, кода два а символ один. Забыл это умное слово. При реверсе у вас будет белиберда.

Например, слово «Нет» по арабски "لا" но если вы будете его удалять, нажать backspace придется два раза. Хоть браузер и выделяет его с двух раз, символ все равно один.
про слова и читаемость условий небыло.
Как писали ниже, там много лигатур и изменений написания в зависимости от порядка символов и положения символа в слове. Если тут найдутся востоковеды, может подскажут, бывают ли вообще у арабов палиндромы и эксперименты с порядком букв.
Работа с лигатурами — требует уточнения задания. Нужно ли считать лигатуру одним символом, или наоборот нужно её разбить, а после инверсии, если возможно, составить новые лигатуры.
Не совсем. Каждая арабская буква существует в пяти видах: «платоническая» буква, отдельно стоящая буква, буква в начале, середине и конце слова.

Соответственно после реверса «буква, стоящая в начале слова» превратится в «букву, стоящую в конце слова» и наоборот.
Соответственно после реверса «буква, стоящая в начале слова» превратится в «букву, стоящую в конце слова» и наоборот.


Это неочевидное поведение, которое должно быть указано в ТЗ.
А история со смайликами или демпозированной «й» — не должна?
Со смайликами — не должна, т.к. это целые символы (хоть и кодируются двумя парами байтов), с декомпозированными символами — должна, но это поведение по крайней мере выглядит логично (декомпозированный символ всё-таки подразумевает собой один символ языка).
А вот приколы типа заглавных букв в начале, парных скобок и т.п. — это уже спецтребование. Потому что реверсируя строку языка — мы не получим валидный текст на этом языке, а гоняться за декорациями в и без того невалидном тексте, без отдельной на той команды, не стоит.
Со смайликами — не должна, т.к. это целые символы (хоть и кодируются двумя парами байтов), с декомпозированными символами — должна
А не объясните чем отличается смыслик от какой-нибудь буквы «о́» в слове «бо́льший».

Ну просто очень-очень интересно что должно быть в голове у человека, который считает то, что описано в статье (и что даже Хабр отказывается за валидный текст признавать) одним символом, а «о́» — двумя.

Потому что реверсируя строку языка — мы не получим валидный текст на этом языке, а гоняться за декорациями в и без того невалидном тексте, без отдельной на той команды, не стоит.
Это правда — но я, как раз, эмодзи бы записал в то, что требует «спецтребований».
А не объясните чем отличается смыслик от какой-нибудь буквы «о́» в слове «бо́льший».

Тем, что смайлик — это целый юникодный символ (кодируется суррогатной парой), а «о́» — это два юникодных символа.
Это правда — но я, как раз, эмодзи бы записал в то, что требует «спецтребований».

Дело не в том, что они эмодзи, а в том, что они — юникодные символы. Символы, кодируемые суррогатными парами — далеко не только эмодзи.
Вот, например блок unicode-table.com/ru/blocks/mathematical-alphanumeric-symbols
Тем, что смайлик — это целый юникодный символ (кодируется суррогатной парой), а «о́» — это два юникодных символа.
Вы вообще-то статью читали? Вот начиная со слов Подождите ка, с недавних пор мы можем указать цвет для смайла, и что же будет, если мы передадим такой emoji в функцию?

Там может быть и один кодпоинт и два (см флаги) и больше (вот эти вот, упоминаемые в статье, чёрные руки и всякие школьные учительницы — это 2-3 кодпоинта).

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

Так-то задача «разверните строку, состоящую из кодпоинтов» — вполне корректна… только она не имеет ничего общего с обсуждаемой статьёй.
Подождите ка, с недавних пор мы можем указать цвет для смайла, и что же будет, если мы передадим такой emoji в функцию?

Да, я, похоже, упустил контекст беседы. Комбинируемые символы нужно либо не обрабатывать вообще, либо обрабатывать все до единого.
Ну, если влоб, чтобы не было приколов с суррогатными парами:
const strReverse = str=>{
let result = [];
for(let a of str){
result.unshift(a);
}
return result.join('');
}

Получилось коряво, но я не успеваю отредактировать в нормальный вид.
Случаи с декомпозицией — отдельная штука, тут нужно писать функции для нормализации юникода.
И всё равно я хрень написал… Пардоньте. Отсюда мораль: не надо торопиться.
const strReverse = str=> [...str].reverse().join('');

Так лучше.
Вроде как то раньше натыкаешься на эмоджи именно в момент сохранения. Я тоже как то удивился почему тип поля utf8_general_ci вроде всепоглощающий не сохраняет эмоджи и требует utf8mb4. А вот так вот.
Вот тоже хапнул фигни с этим и пришлось менять кодировку в базе.

utf8 сохраняет максимум 3 байта на символ, поэтому те же emoji там приводят к некорректной utf-8 кодировке. utf8mb4 уже полноценный, так назвали скорее всего из-за того что "правильное" название было уже занято. Вот здесь к примеру есть немного разъяснений.

Какой классный язык, в котором нельзя быть уверенным даже в примитивный функции split() (по словам автора)

Про комбинационную диакритику вы уже всё поняли (кстати, привет эппловцам! некоторые приложения на маке вместо и-краткого делают и+кратка, повбывавбы!!!)


Следующий челлендж — это корректная работа с справа-налево и двусторонним текстом.
И вот тут я хз, какое должно быть ТЗ. То есть — как оно вообще правильно?


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


Oh wait… Самый простой способ перевернуть строку — это добавить управляющие коды биди!!!


Единственно, что такой переворот будет необратимым. Reverse(Reverse(s)) != s.
Опаньки… А ведь в ТЗ про это не говорилось. Любишь подразумевать — люби и расхлёбывать.


Ах да, на сладкое. Есть такие штуки, как лигатуры. Помимо того, что некоторые лигатуры имеют свои собственные коды (например, œ), а некоторые не имеют (например, ij), — так ещё и от шрифтов зависит, будет ли пара символов f+i отображаться одним глифом или двумя.
Поэтому — следует ли слово "fifi" перевернуть в "i f i f" или в "fi fi" ?




Ну и совсем на сладкое. Загадка для любителей переворота строки.
Очевидно, что предикат IsPalindrome(s) = (s == Reverse(s)).
Вопрос: корректно ли он сработает на строке "(((а роза упала на лапу азора)))" ?

Любишь подразумевать — люби и расхлёбывать.
вот это, с вашего позволения, я в свой блокнотик утащу, пригодится
Вопрос: корректно ли он сработает на строке "(((а роза упала на лапу азора)))" ?

Не совсем, косяки с пробелами — ароза упал ан алапу азор а

Скобки — это самое главное заподло в этой строчке ;)

Почему западло? Ну будет )))ароза упал ан алапу азор а(((
Задача же стоит просто строку развернуть. Задание выполнено. А функция палиндрома вернёт ошибку даже без скобок, из-за косяков с пробелами.

Если будет отдельно сказано сохранить функцию скобок — круглых, квадратных, фигурных и угловых — тогда будем работать над этим

Так в этом и фокус. Цикл разработки:


  1. Написал функцию проверки на палиндромность.
  2. Написал юниттесты на чётное и нечётное количество символов, на пустые строки, всё работает, доволен как слон.
  3. Отдал тестировщику, он скормил типичный палиндром про лапу Азора.
    Недолго думал, сделал пробело- и регистронезависимую реализацию, добавил Азора в юниттесты, всё работает, доволен как слон.
  4. Отдал тестировщику, он скормил палиндром с кавычками: «А роза упала на лапу Азора» (скопипастил из своего чеклиста в ворде).
    Долго думал, сломал глаза, понял, пошёл ругаться с тестировщиком и архитектором на предмет ТЗ.

Скобками можно троллить соискателей:
Является ли палиндромом строка


  • ((((
  • (())

Кстати о биди. Алгоритм рендеринга биди-текста должен уметь самостоятельно разворачивать скобки в нужную сторону!
(А умеет ли он разворачивать кавычки с учётом национальной типографики — это хороший вопрос… Русские и английские лапки несовместимы, и это типичная ошибка при дизайне шрифта — вместо верхней bb-лапки сделать верхнюю pp-лапку, а потом русский курсив будет пускать кровь из глаз).

Unicode уже давно развивается в неверном направлении. На языке разработчиков feature creep называется. Куча ненужных символов, создающих только путаницу и непоследовательность применения. Отсутствие ряда важных языковых нюансов. Колоссальная избыточность приводит к отсутствию полноценной поддержки со стороны шрифтов и ПО. По-хорошему все эти недоэмодзи нужно заменить полноценным микроязыком разметки со стандартизованными средствами форматирования, вставки изображений и спорадических неконвенциональных знаков…
UFO just landed and posted this here

Судя по комментариям, я один считаю автора снобом?


Начнем с первого вопроса. Буду говорить о Python, так как считаю себя экспертом в Python. Пишу на нем около 20 лет (начинал тогда, когда в России о Python еще никто даже не слышал). Сделал на нем множество проектов самой различной сложности. От CLI утилит до сайтов до десктопных GUI приложений.


Когда я только начинал писать на Python, то проштудировал от корки до корки все, что шло в поставке: Python Tutorial, Python Reference (где описаны формальным языком все конструкции языка), Python Library Reference (а она в Python большая была уже тогда, не то, что отсутствующая stdlib в Javascript).


Конкретно помню, что я заучивал специально, например, таблицу приоритетов операторов.


И вот статья автора заставила задуматься: а что же вернет type(None). И хотя когда-то я наверное это знал, то сейчас пришлось полезть в консоль, чтобы узнать результат. Хотя это за 20 лет самых разных проектов ни разу мне не пригодилось и с вероятностью 100% никогда не пригодится. Более того, если я увижу этот вопрос спустя год, то скорее всего, мне снова придется лезть в консоль, потому что я уже не буду помнить. Потому что это ненужная в повседневной работе информация, и потому она будет вытеснена другой в течение месяцев.


Значит, по мнению автора, я всего-навсего джуниор?


Или вот точно помню, что учил таблицу приоритетов 20 лет назад. Смогу ли сейчас правильно выписать её по памяти? Уверен, что нет.


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


Более того, представим, что автор статьи передал обязанность проводить собеседования кому-то другому на пару лет (и слава макаронному монстру за это!). Более того, он наконец решил перейти на нормальный язык программирования, пускай, Clojure, и два года писал только на этом языке. А затем вдруг решил перейти в другую компанию и вернуться к Javascript. Пришел на собеседование к копии самого себя двухлетней давности и получил те же самые вопросы. Сможет ли он пройти собеседование и не пасть позорно до джуниора споткнувшись на собственных же вопросах двухлетней давности?..


А если интервьюер еще попросит его привести формальные определения всех нормальных форм? Как в Javascript вычислить sha256? Приоритет операций?


Да существует миллион способов унизить собеседуемого и показать собственную немерянную крутость. Только не думаю, что это имеет отношение к разделению на джуниоров, миддлов и сениоров.

Спасибо за комментарий, отвечу вам.

Порой (очень часто) на собеседования приходят молодые люди, претендующие на позицию (и зарплату) сеньера, не имеющие за своими плечами многолетнего багажа опыта — пара курсов, пара стартапов, пофрилансил, потом еще стартап и все, сеньер! Нет, не сеньер!

Я повторюсь — понятие джун/миллди/сеньер — субъективное. В этом комментарии — я говорю о своем восприятии.

Чуть отвлекусь на тему typeof null — нету в js нормального способа проверить является ли значение переменной объектом. Или юзать готовые решения, либо писать функцию самому. Вот оптимальная реализация:

function isObject (value) {
    return value && typeof value === 'object' && value.constructor === Object;
}


И вот здесь и кроется ответ на вопрос: зачем я спрашиваю про typeof null на собеседовании — typeof null === 'object'!

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

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

Сможет ли он пройти собеседование и не пасть позорно до джуниора споткнувшись на собственных же вопросах двухлетней давности?


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

Искренне надеюсь, что смог донести до вас свою мысль.

Позвольте я упрощу ваш высокосеньёрный код и поправлю, чтобы он не возвращал что попало вместо boolean:


function isPOJO (value) {
    return value ? value.constructor === Object : false
}
Порой (очень часто) на собеседования приходят молодые люди, претендующие на позицию (и зарплату) сеньера
Вот это ключевое. Надо сбить зп.
Разве можно этому молодому щеглу платить почти как мне?! Ну ка переверни мне строку с арабскими смайликами и перфокарту прочитай. А то вдруг РКН запретит JS или ещё какой катаклизм случится.

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


Сори, конечно, за откровенность. Но в случае отрубания интернета РКНом «проверить, что там вернул бекенд» в списке приоритетов будет занимать исчислимую на пальцах позицию снизу списка (сначала проблему доступа к репозиториям решите, например). Не говоря уж о катаклизме, в случае которого этой проверке в списке приоритетов места не найдется совсем.
Окей, предположим, что РКН вправду заблокировал lodash и что-то там про типы теперь знать нужно.

Но зачем строку-то переворачивать? Это реально нужно в каком-нибудь проекте? Вы на вашей работе хоть раз переворачивали строку в промышленном коде?
var d = Object.create(null, {'d': {value: 42, writable: true}})


typeof d === 'object'
isObject(d) === false
У того же Лодаша — есть целыз 3 ф-и для проверки на объект, и только одна из них обрабатывает ваш кейс.
isObject — нет
isObjectLike — нет
isPlainObject — да
как по мне, неплохой вопрос сеньору, большой пласт поднимает
Гораздо более интересный вопрос сеньору (не автору, а нормальному сеньору) — это «A зачем вам нужна проверка isObject()? В какой реальной ситуации вы намерены ей воспользоваться, и как именно? Почему в этой ситуации нельзя будет использовать is<более узкий тип>()?»

PS: А еще меня забавляет ирония ситуации, в которой в ветке комментов два (скорее всего хороших) программиста пишут функции isObject() и isPOJO(), которые возвращают значения, совсем не обязательно соответствующие своему названию.
С этим я тоже согласен, практическая применимость такой функции сомнительна. Я больше на тему «хорошо бы знать, как создаются объекты и в чём может вылезти разница» — вот это сеньору неплохо бы понимать.

По мне, если соискатель ответит на вопрос «нет ли подвоха в этой функции», даже без примера, то это прекрасно.
Да вон же несколькими комментариями выше вам его сломали.
Если копать вглубь, в JS вы всегда дойдете до duck typing — ну и гляньте, что вы проверяете в вашем методе с точки зрения duck typing. Что у него конструктор определенного вида есть? А занафига мне это для POJO? Когда я вижу «POJO» в яваскрипте, я ожидаю, что у меня есть возможность читать и писать свойства — и как вы это проверили в вашем методе? Да никак. Итого у меня есть
var veryMuchPOJO = Object.create(null);

в который я легко могу писать и читать свойства, а вы своей функцией мне говорите «false».
Ваша функция точно не будет вызывать вопросов, если она будет
function surelyIsAPOJO()

Тут по названию видно, что могут быть ложноотрицательные срабатывания ;)
С чего бы?

Давайте так: с точки зрения спеки у нас нет типа «POJO», поэтому все измышления о статическом типе можно на этом и закончить.
А с точки зрения duck typing критерий POJO — это возможность читать и писать свойства. Всё.
Как оно создаётся — вообще не важно. Если вы хотите продолжать утверждать, что важно — у вас тогда
var totallyPOJO = Object.create(Object.prototype)

будет не POJO. Не литерал же.

Есть то, что в индустрии принято называть POJO. Выделяется оно потому, что его удобно создавать прямо в коде без предварительного объявления, и он переживает сериализацию через JSON без плясок с бубном. Так что давайте вы не будете высасывать собственные бесполезные определения из пальца.

Есть то, что в индустрии принято называть POJO.

Yep. И, как я уже говорил, это штука, куда пишутся свойства и откуда читаются свойства. Не просто так, конечно, а с определенным контрактом (а иначе JSON.stringify и прочие не взлетят). И до тех пор, пока этот контракт выполняется — вы имеете дело с POJO.
Как оно при этом создавалось — вообще не имеет значения.

«Так пишут на SO» — аргумент, мягко говоря, не очень хороший. Попробуйте что-нибудь еще.

PS: На всякий случай — созданное через Object.create переживёт сериализацию в JSON просто на ура. Если вдруг вам это не очевидно.

PPS: Вы, я надеюсь, понимаете, что я эту беседу веду совсем даже не с позиций применимости. В мире не больно много любителей посоздавать объекты без Object.prototype в цепочке, и их всегда можно послать подальше, даже если они вдруг вам встретились. Беседа тут о том, что неплохо бы вот этот разрыв в типах осознавать. Просто что он существует. И что когда вы применяете фактически duck typing, но проверяете не нужную функциональность, а какие-то совершенно неважные свойства — вы немножечко так лукавите в коде.

Я вам по секрету скажу, что Object.freeze({}) — это тоже POJO. Ещё раз — не выдумывайте свою терминологию, иначе вас никто не будет понимать.

Конечно. И? Вы всерьез считаете, что я в нескольких словах («читать и писать свойства») вам смогу описать корректно всю спеку? Если вам не нравятся словесные приближения, то я далее начну писать «соотвествует спеке» вместо этого.

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

И это мне говорит человек с терминологией «POJO — это когда через литерал».

Для особо одарённых: Любой POJO может быть создан через литерал, либо эквивалентный использованию литерала код.


Для ещё более одарённых: POJO — объект, имеющий тот же прототип, что и любой литерал.


На этом давайте закончим нашу специальную олимпиаду.

UFO just landed and posted this here

Чудесно, вы нашли одно единственное исключение. Можете купить себе медаль.

typeof null === 'object'!
— Это официальный признанный баг языка, оставленный для совместимости. Хотя, null, на самом деле, это отдельный тип данных.
Всё, я могу претендовать на зарплату сеньёра?)
Null — это спецзначение объектного типа, обозначающее отсутствие ссылки. Всё более чем логично.
Так же как NaN это спецзначение типа с плавающей точкой.
Есть хорошая картинка, объясняющая, что такое Null )

image

Для полноты картины нужно добавить undefined — это когда держателя тоже нет.
нет, это когда схватился руками за что-то неясное и не открываешь еще глаза
Я, возможно, ненастоящий сеньор, но за 12 лет работы мне нужно было определять «объект ли переменная» раза четыре, и задачи это были специфические (типа проверки соответствия произвольного объекта схеме). Даже реверс строки мне чаще пригождался :)
Я, безусловно, держу в голове закладку на тему «в js надо аккуратно с undefined и null», но помнить наизусть все детали (в том числе что там возвращает typeof null) — голова треснет. Это такая вещь, которую я могу в консоли проверить в любой момент. Мне достаточно помнить, что особенности есть, но не вижу смысла зубрить конкретику.

Как бы вы отнеслись к ответу «не помню точных деталей, но если мы обрабатываем произвольное значение заведомо неизвестного типа, то лучше на null и undefined проверить отдельно в самом начале — наверняка у нас какая-то отдельная логика для этого случая должна быть»?
Отнесся бы положительно и спросил бы о вашем опыте, когда результат был неочевиден, поделился бы своим.
Как проверит, что другой молодой и талантливый не передал в его функцию невалидное значение?


Напишет unit тесты и увидит, что для null работает не как ожидалось?
А в unit тесте — как проверит?)
Передаст null.

Это логично, что когда ты написал функцию, которая называется isObject(), ты пишешь на нее тест и передаешь туда массив/объект/строку/число/пустой_объект/undefined/null.
Вот не будет завтра lodash, отрубит нам РКН интернет или еще какой катаклизм случится.
Извиняюсь, но оторванный от реальности дурацкий аргумент.
В том же стиле можно и так рассуждать: «Не будем его нанимать, он же смертный, он можеть умереть завтра.».

Если бы мне кто-то правильно отвечал на все эти тонкости языка, я бы подумал в следующем порядке:
1) Он новичок, только недавно все это выучил.
2) Он недавно хорошо подготовился.
3) Он наверное работает все время с одним и тем же, не развивается, не писал функции, которые упрощает подобные проверки, поэтому хорошо помнит основы, тонкости.
4) Крайне маловероятно, но может он обладает феноменальной памятью.
5) Ладно, ладно. И вправду, крутой специалист. Наверное ботан какой-то, который кроме компьютера ничего в жизни не видел.

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

Не в ваш адрес, а просто в тему обсуждерия.


Операция type(None) лишена смысла, потому что для проверки на None используют is None, что очень дешево. И то же самое в других языкак. Поскольку не может быть несколько разных null, nil, None, то проще сравнить значение, а не тип.

Об этом же написал чуть выше — это тот случай, когда теория будет суха, а древо программирования зеленеть будет от тысяч часов практики. Мозг будет выбрасывать со временем ненужные знания, оставляя готовые кейсы. Уже пишешь не задумываясь, просто мозг уже знает, что просто так надо
Уже пишешь не задумываясь, просто мозг уже знает, что просто так надо
Это неплохой навык, но иногда стоит остановиться, посмотреть на то, что ты написал и подумать на тему «а почему ты так это написал».

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

Или, образно: да, практика поливает ваше «древо программирования», но иногда нужно перестать лить воду и убедиться что корни не сгнили…

Всё время пока читал статью думал, что меня тут смущает. Наверное то, что надо для начала определить для себя понятие "строка". Если мы хотим хранить там смайлики, то это уже сериализованная последовательность объектов, для которой должен быть написан соответствующий контейнер, итераторы и всё необходимое, чтобы можно было применить стандартный алгоритм reverse.
С таким же успехом можно считать элементом строки например слово и делать реверс по словам.

Зря вы так. Вопрос правильный.

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

Проблема в том, что за каким-то чудом последовательности кодпоинтов стали изображать визуально слитно. И если в применении к алфавитным символам это (с большим скепсисом) еще можно принять за необходимость, то составные свистелки «эмоджи» — просто за гранью добра и зла.
Не хотели расширять формальную спецификацию UTF и для этого… барабанная дробь… расширили её, введя составные иероглифы.

Таким образом, реальный парсер UTF теперь в любом случае должен содержать дерево всех допустимых комбинаций в виде структуры или конечного автомата — уже не суть.
Локальные проблемы типа тех, что JS split() не использует правильный парсер, всего лишь следствие того бардака, что устроили с прекрасной кодировкой UTF любители слать картинки непременно негритянской руки текстом.
Зря вы так. Вопрос правильный.
Именно! Я про то и говорю: задание было —
Напишите функцию, которая принимает на вход строку, а возвращает эту строку «задом наперед»
А теперь мы насмерть умучиваем задающего, заставляя его объяснить, что такое, по его мнению, «задом наперед», или
что именно считать «элементом» строки
с учётом 100500 граничных случаев.
а иврит с огласовками ваш алгоритм обработает?
שָׂרָה שָׁרָה שִׁיר שָׂמֵחַ, שִׁיר שָׂמֵחַ שָׁרָה שָׂרָה

идея в том, что сначала пишется буква, а потом символ огласовки. причем иногда их может быть 2 (как в первой букве). Да, и всё это — справа налево.

Да, а если туда еще и числа вставить, то вообще жуть получится. Потому что числа в иврите пишутся слева направо.
נולדתי ב -1982
я родился в 1982 году.
Ну с числами-то как раз всё будет нормально, а вот огласовки применяться на другие буквы. Unicode потихоньку превращается в тьюринг-полную хрень (что, в принципе, не так уж и плохо). Потому такие задачи так же потихоньку упираются в задачу остановки (выполнить алгоритм наоборот — равносильно убедится, что он не повиснет) — и, следовательно, становятся принципиально не разрешимыми в общем случае.
Давать на собеседовании задачу на тонкости юникода — это издевательство.
Ну, про суррогатные пары — стыдно не знать. Это классика из классик, на протяжение по крайней мере 10 лет на всех форумах.
UFO just landed and posted this here
По JavaScript-у. Всюду, где тусят начинающие JavaScript-еры, эта тема поднимается.
>На каких форумах?

Форумах любителей выражать свои мысли через эмодзи, конечно.

(Кстати, форма отпраки коммента на хабре эмодзи не поддерживает — только что проверил).
Получилось вставить emoji, но текст за ним не отображается. Что-то бегает по комментам.

Вставить-то получается, а опубликовать — нет.

На хабре, как я заметил, не только эмодзи, но и другие суррогатные пары не поддерживаются.
Компромисс в данной ситуации — если собеседуемый скажет «вот реализация, но с суррогатными парами она работать не будет», а собеседующий это примет. Если первый не вспомнит, или второй заставит это непосредственно реализовать — вот это уже будет плохо.
Простое правило счастливой жизни:
Не ходите в проекты/организации, где разработчикам требуется «typeof null»

Нервы с годового бонуса назад не откупишь.
> чему равен typeof null?

Ну наконец то найден вопрос которым сразу можно отличить джуна от сеньора! (нет)

> Вот не будет завтра lodash, отрубит нам РКН интернет или еще какой катаклизм случится.

РКН отрубит доступ разрабов к их жестким дискам что ли? Просто не могу себе представить такого сценария, что lodash там или jquery хоп и пропал. Разьве, что по электромагнитной бомбе на все города сбросить. Но в этом случае, проблема отсутствия lodash вряд ли будет кого-то волновать.

> Что касается сеньеров — еще более субъективно, мне кажется, что сеньеру было бы интересно знать, почему это работает именно так, а не как-то иначе, вот и все.

Кому-то это интересно, кому-то другое, а таких нюансов в каждом языке, фрейморке, БД, протоколе милллион. Всего не изучишь, да и смысла нет, тем более что все это меняется каждые два года. Специфика работы у всех разная. Справшивать, не понимая, чем человек вообще раньше занимался не правильно. Вот например начинаешь спрашивать человека по SQL и он валиться на group by having. Значит ли это что это джун? Нет, не значит, надо для начала спросить, с какими БД он вообще работал или в чем заключалась его работа.

Надо спрашивать не что человек не знает и не умеет, потому что таких вещей бесконечное множество, а что он знает и умеет.

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

РКН отрубит доступ разрабов к их жестким дискам что ли?

Не подсказывайте им!!!

мой любимый вопрос это: «чему равен typeof null?»


Это нечестный вопрос. Пригождается очень редко. Забывает очень быстро. Код, который полагается на такое поведение, должен комментироваться. Я забуду через неделю, что возвращает typeof null и мне за это не стыдно. Я не могу каждые полгода перечитывать спецификации Javascript только для того, чтобы быть способным ответить на этот вопрос или для того, чтобы приготовиться к случаю (цитата) «Вот не будет завтра lodash, отрубит нам РКН интернет или еще какой катаклизм случится».

Вообще, завалить любого человека на edge-кейсах очень просто. Даже если он реально сильный разработчик с богатым опытом.

У меня вот любимые области, с которыми я много работаю и о которых много читаю, — это особенности HTTP протокола и безопасность в вебе. Это огромные темы, на которые написано много материала и в которых происходят постоянные изменения. Браузеры/сервера постоянно меняются. И эти темы имеют непосредственное отношение к веб-разработке.

Я уверен, вам было бы неприятно, если бы я начал собеседование с заковыристых вопросов по хеадерам, потом скажу, что это вот используется в наших проектах, и раз вы не знаете ответ на этот вопрос, то возможно вы даже до джуниора не дотягиваете.
UFO just landed and posted this here
>Результат typeof null == «object» – это официально признанная ошибка в языке, которая сохраняется для совместимости. На самом деле null – это не объект, а отдельный тип данных.

Что может быть лучше чем компания намеренно полагающаяся на обратную совместимость древних багов в интерпретаторе.
Я с вами не соглашусь.

Во-первых, до использования typeof можно явно проверить на null. Прям через оператор сравнения. Это будет явно и очевидно для всех читающих.

Во-вторых, я такие вещи обязательно покрываю unit тестами. Если есть какое-то не очевидное поведение JS, о которым я забыл, я об этом сразу узнаю. Уж на null в тестах проверить можно.

Возможно, есть специфичные проекты, где проверка typeof null — это нечто обыденное, что встречается часто, и без знания этого с проектом просто нельзя работать. Но я в таких проектах пока не работал (что, конечно, не значит, что их нет). Но даже там при ревью тебе коллеги напомнят/подскажут об этой вещи, если эта такой частый у них кейс.
UFO just landed and posted this here
Проверка на null даст значительно больше информации, чем проверка на typeof null.
Отдельно Вам доставит (скорее всего, проблем) попытка поработать с null как с объектом
a = {}.field     //undefined
b = null.field   //TypeError: null has no properties
c = null
d = c.field      //TypeError: c is null

UFO just landed and posted this here
Гораздо лучше знать, что если typeof кем-то применяется для чего-то кроме === 'string' и === 'number', то скорее всего код делает не совсем то, что от него ждут.

И даже если он применяется для проверки на строку и число, он всё равно может делать не то, что от него ждут (впрочем, этот случай уже куда менее вероятен).
Налицо явный баг в split: developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/split#Syntax
str.split([separator[, limit]])

Параметры
Раздел

separator
Необязательный параметр. Указывает символы, используемые в качестве разделителя внутри строки. Параметр separator может быть как строкой, так и регулярным выражением. Если параметр опущен, возвращённый массив будет содержать один элемент со всей строкой. Если параметр равен пустой строке, строка str будет преобразована в массив символов.

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

Полагаю, вы уже зарепортили этот баг?
Загляните в английскую версию, там есть приписочка:

Attention: If an empty string ("") is used as the separator, the string is not split between each user-perceived character (grapheme cluster) or between each unicode character (codepoint) but between each UTF-16 codeunit. This destroys surrogate pairs.

Мини-хабра-самоубийство


мой любимый вопрос это: «чему равен typeof null?»

Прекрасная иллюстрация полезности статической типизации

UFO just landed and posted this here

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


В JS же, судя по всему, применен паттерн Null object.

Всё встаёт на свои места, если вспомнить, что в JS типизация таки есть. Хоть и динамическая.
Значение null означает, что переменная имеет объектный тип и нуллное значение.

Что куда встает?
Судя по статье, в JS сеньорам приходится запоминать табличку исключений и помнить, что "есть особенности". Рантайм съест нулл и не подавится.


В этом смысле идеальны языки со строгой типизацией, где компилятор будет тыкать носом разработчика еще на этапе написания кода. Вместо того, чтобы помнить табличку исключений, разработчику нужно думать: всегда ли тут существует значение? Что делать, если оно отсутствует?

UFO just landed and posted this here
Значит статически типизированные языки не такие уж и статические? :)

Статические, но недостаточно строгие.


Почему не тыкает? Код выполняться не будет.

Потому что типизация статическая, но не строгая.


Так то и в scala со строгой типизацией тоже можно NPE поймать, но язык задуман так, чтобы все источники null завернуть в Optional и вывести на периферию I/O. В бизнес-логике нуллов вообще быть не должно. Там, где значение необязательно, используется Optional.


Вот и получается разница: в js/java у вас может что-угодно где-угодно оказаться нуллом и вы не обязаны писать обработку этих случаев.
В scala все что у вас может оказаться null, у вас Optional и вас компилятор заставляет учитывать такие случаи.

А вот и приходится думать, потому что компилятор с null не хочет помогать:

Да вроде хочет:


image

UFO just landed and posted this here
Ну это очевидный контрпример к
А вот и приходится думать, потому что компилятор с null не хочет помогать

Вполне вон хочет и помогает. Язык вроде не фиксировался.
UFO just landed and posted this here
Получается, что язык вроде бы статически типизирован, но я тоже не могу доверять этой типизации и должен у себя в функции проверять someObject на null?

Все так. Java вроде статически типизирована, но при этом типизация недостаточно строгая, чтобы компилятором выявлять проблемы с null.


Null object — это совсем другое.

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

UFO just landed and posted this here

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

UFO just landed and posted this here
У null нет типа. Вы можете присвоить его объекту любого типа, и объект любого типа сравнивать с null.
UFO just landed and posted this here
При этом языки заявляются как строго статически типизированные. Не является ли это введением в заблуждение?

Если под "этими" языками понимать java и c#, то они типизированы статически, но не так уж строго. Есть куда стремиться.

UFO just landed and posted this here

Строгость не бинарна. Грубо говоря, это количество правил, в которые компилятор будет вас тыкать носом. Чем больше правил, тем строже.
У java/C# компилятор просто сильно строже, чем в js.
У scala компилятор еще строже, чем в java.

UFO just landed and posted this here

Тем что даже в java и c# null это полное отсутствие значения, противопоставляя тому что в js это значение "ничего".

UFO just landed and posted this here
Тем что даже в java и c# null это полное отсутствие значения

но не отсутствие типа переменной.
противопоставляя тому что в js это значение «ничего».

А вот тут Вы ошибаетесь. Никакого противопоставления тут нет. А логика в JS точно такая же: null — это отсутствие значения, но не отсутствие типа переменной.
Языки разные бывают. В том же C++ можно передавать объект по ссылке, но ссылка не может принимать значение null. Или же non-nullable типы в C# 8. Вот и примеры более строгой типизации.
UFO just landed and posted this here
Вот здесь очень подходит слово обнулить ) Просто удаляем ссылку на значение переменной, очищая память, присваивая переменной значение null
забавно, что

typeof null


при этом отвечает

object
Это только в JS так. Более того, это баг.
Не вижу признания авторов языка, что это ошибка.
Null — это спецзначение объектного типа (так же, как в других языках). Логично, что оператор, возвращающий тип данных переменной, в случае
let v = null;
typeof v

возвращает «object» — переменной присвоен объектный тип, но не присвоено никакой ссылки.

Применение же typeof к константе — это гнусное извращение, ни один язык не обязан с таким работать.
Не стану упорствовать по поводу бага. Хоть это и укоренившееся мнение, оно вполне может быть ошибочно. Тем более, что в спецификации сказано что typeof null это «object», т.к. спецификация составлялась в соответствии с существовавшей на тот момент реализацией. И поэтому нельзя считать это багом в классическом понимании.
Null — это спецзначение объектного типа (так же, как в других языках).

Далеко не во всех языках это так. Он вполне может быть отдельным типом. Например:
Python
x=None
print(type(x))
//выведет <type 'NoneType'>


Dart
var x = null;
print(x.runtimeType);
//выведет Null


Спец значением он является в таких языках как Java, C# или C++. Впрочем, в C++, nullptr является специальным значением указателя, и к объектам отношения не имеет, т.к. указатель может быть на любой тип. Вероятно, Java позаимствовала семантику null из плюсов, и натянула на свою объектную модель, в результате чего он и стал специальным значением объектного типа.
Впрочем, в C++, nullptr является специальным значением указателя, и к объектам отношения не имеет, т.к. указатель может быть на любой тип

Это зависит от реализации. В общем случае это std::nullptr_t, который может быть чем угодно.

А ведь действительно. Выходит, что в современном C++ это не только специальное значение, но и отдельный тип указателя.
так же, как в других языках

Ну-ну. Напишите в C++


auto v = nullptr;

и посмотрите, какого типа будет v.


Ну или в C#:


var v = null;

будет ещё интереснее.

Ещё больше смешных вопросов для собеседования:

достаточно ли вы умны чтобы работать в google

P.S. Ну я понимаю, когда просят написать генератор паролей юзеров со встроенной мнемотехникой или сломать что-нибудь. А это и правда немного странно.

Эм… Я достаточно умён, чтобы не покупать книгу с подобным дизайном.
Я не писал слово «покупать». Книга мне понравилась меньше, чем я полагал, но забавная, интеллект развивает.

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

Ради расширения кругозора: Swift один из первых (известных мне) языков, кто решился уйти от UTF-16 с его суррогатными парами и по умолчанию работает с юникодной строкой как с набором глифов. Из-за чего привычные способы работы с моноширинными строками у него не работают. Но задачка из топика решается тривиально (String("...".reversed())).

Помню, когда только начинал знакомство со свифтом через CodinGame, в части задач очень ругался, что не могу «как в нормальных языках» charAt(int) делать. Зато вот на подобных примерах как раз очень хорошо видно, что это действительно не баг, а фича.
> Напишите функцию, которая принимает на вход строку,
> а возвращает эту строку «задом наперед»

мы считаем что спрашиваемый умеет чтитать мысли и сам догадается что
— писать надо на каком то дибильном языке програмирования
— у аффтора вопроса свое болезненное понимание «оптимальности»
— хотя в вопросе вооще ничего нет про оптимальность
— но видимо обычный цикл for (длина строки..) — неправильный ответ
— рекурсивный алгоритм видимо тоже неправилен, (думаю аффтор наверно не понимает такого)
— ОППА сюрприз — аффтор считает что вызов reverse() это Нормально!
— аффтор болезненно влюблен в «const»

(давайте может просто спросим про гольфовые мячики в скулбасе?)
Автор узнал, что такое кодировка, и спешит поделиться этим со всем. Кликбейт-заголовок прилагается.
UFO just landed and posted this here
В каком браузере? Какой код у этого символа?
Я как потомственный старпер и пескосып нечеловечески страдаю на утверждении, что оптимальным способом развернуть стоку является применение к ней функции reverse. Пусть даже и от массива, а не от строки.

В вашей крови опасно низкая концентрация хардкора, коллеги.

(хардкор ниже умеет й и ё, но ломается на эмоджах, как старпер этим даже немножечко горжусь)

rev = (s,n) => (n||0)<(s.length-1)/2 ? rev(s.substr(0,(n||0))+s[s.length-(n||0)-1]+s.substring((n||0)+1,s.length-(n||0)-1)+s[n||0]+s.substr(s.length-(n||0),s.length), (n||0)+1): s;


дергать:

rev("Habr")
UFO just landed and posted this here
В 99% случаев не только не пропустил бы, но еще и устроил бы разъяснительную беседу насчет поддерживаемости кода.
Оставшийся 1% приходится, в основном, на случаи когда разворачивание строки и есть весь проект, не имеющий, к тому же, коммерческой ценности.

Но поскольку с самого начала речь идет о задачке для собеседований, я полагаю что принципы промышленного программирования можно пока оставить в стороне.
UFO just landed and posted this here
It depends. Скажем так, на вопрос об оптимальном способе развернуть строку я бы ждал уточняющего вопроса «можно ли использовать Array.reverse?». И дал бы на него утвердительный ответ, скорее всего.

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

Если он не работает — претендент амбициозен, но переоценивает свои силы, можно брать на позицию джуна, если есть кому плотно за ним присматривать.

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

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

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

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

На собеседовании проекта нет, соответственно нет всех этих 80% требований, который на всех предыдущих проектах в которых человек участвовал, могли радикально различаться.

А требовать ловить все возможные null pointer'ы в функции из пяти строчек, про которую автор точно знает, что ни в какой продакшен она не пойдет, и я знаю что он это знает, это странно.

Вопрос вида «Этот код предполагает уйти в прод, какие вы видите в нем проблемы? Выпустите вы, лично, его в прод, или нет?» я лучше задам отдельно.

Разворот строки это отличная задача для собеседования, она интересна тем что для успешного решения собеседующему даже не нужно писать код. Точнее тот кто сразу бросится писать код успешно провалит эту задачку и сразу будет видно что перед тобой джуниор. Когда же сеньор-разработчик вместо того чтобы начать писать код сразу задаст кучу уточняющих вопросов — что такое строка? Это список двух-байтных символов в представлении js или же это список код-символов юникода или что-то посложнее (графем и т.д)? И зависимости от ответа сеньор будет задавать уточняющие вопросы — а входит ли в понятие элементов строки всякие управляющие символы или символы из разных категорий и т.д.
И я думаю по похожему принципу можно составить кучу других задач которые смогут точно определить кем является собеседующий — джуниором который нахватался отдельных знаний (или натаскал себя на решения типичных задач для собеседовании или даже заучил как студент перед экзаменом без понимания что к чему) или сеньор у которого в голове знания разложены по полочкам и составляют единую картину

Когда же сеньор-разработчик вместо того чтобы начать писать код сразу задаст кучу уточняющих вопросов

Уточняющие вопросы? Их есть у меня!

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

Это даже не поможет вам пройти интервью в другую компанию, так как код-то в результате написан не будет…

P..S. У нас, например, есть в отчёте об интервью отдельный раздел, куда нужно вставлять код, написанный кандидатом. И если его там нет… то это сокращает время работы всех, кроме собственно интервьюеров, почти до нуля. А уж если там пометить, чтобы его в чёрный список внесли и в будущем на интервью не приглашали — то экономия ещё больше (но тут одного такого интервью не хватит). Так что для фирмы такой подход неплох — но вопрос «а зачем интервьюируемый вообще на всё это дело время тратит» остаётся…
Добросовестный джун будет шерстить интернет в поисках таких вот мелких вопросов на «особенности», когда синьер понимает что эта дичь пригодится раз в год на минуту и сразу же после благополучно забудется. Порядочных разрабов отсеете больше чем бестолковых джунов-заучек.
Я обычно даю какую-нибудь задачку на то, с чем кандидат будет сталкиваться довольно часто, типа такой (я работаю с 3д-графикой поэтому тригонометрия крайне важна):
Задачка
Есть дверь, которая расположена в точке P1 и базисом [DoorRight, DoorUp, DoorForward] (DoorUp == GlobalUp). У двери есть 2 анимации открывания clockwise/counter-clockwise. К двери подходит персонаж с координатой в точке P2 и базисом [PlayerRight, PlayerUp, PlayerForward]. Определить какую из анимаций двери нужно запустить.

Она имеет как минимум 4 хороших варианта решения и если кандидат минут за 10 находит любое — голова варит, можно брать: нюансам синтаксиса научится, а вот пространственное мышление прокачивать нужно гораздо дольше.

Articles