Comments 419
А ещё лёгкое недоумение тут вызывают методы собеседования автора. Приходишь ты такой на собеседование, а тебя спрашивают о знании стандарта, который позволяет поменять зелёную и коричневую какашки из юникода местами. [::-1] тоже, внезапно, не переварачивает html-страницу вверх ногами, не возвращает аргументы javascript-кода по return-value и не делает меня молодым: так может ещё об этом спросить?
оказывается в стандарте юникода есть вариант объединения не только emoji, но и просто символов
Подождите. Я ведь правильно понимаю, что точки над 'ё' — это типичная диакритика, и если ваш алгоритм не работает с 'ё', то он также сломается на «й» (и + ˘) и скорее всего не работает с банальными немецким и французским?
Неужели эмодзи используются настолько более часто, чем диакритика?
Я немного ввел в заблуждение, простите. Речь идет не о букве "Ё", а о сочетании 2х символов Юникода u{0415}(Е) и u{0308}("̈), которые идя друг за другом образуют последовательность юникода и мы видим букву "Ё" на экране.
\u{0415}\u{0308}' === 'Ё' // false
Prelude> no = "لا"
Prelude> putStrLn $ reverse no
ال
Prelude> putStrLn $ reverse $ reverse no
لا
Prelude> putStrLn $ reverse $ reverse ("Торт " ++ no ++ " Habr")
Торт لا Habr
Prelude> putStrLn $ reverse ("Торт" ++ no ++ " Habr")
rbaH ال троТ
Люблю нормальные, продуманные языки.
И за это разработчиков макос хочется больно пинать ногами
[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' не изменяют выдачу, т.к. считают значок «кратка» печатным символом.
А подход файловых систем Linux — набор байтов, это набор граблей.
Место для записи байт — бинарный файл.
Имя файла — это просто поле в его метаданных, и в него можно (и иногда нужно) записывать байты. Мне правда нужно привести пример кода, который будет в абсолютно неочевидном месте сегфолтится из-за нормализации?
Тогда весь софт который хочет работать с файлами должен его нормализовывать самостоятельно. Мы же программисты, зачем нам копировать одно и то же поведение из программы в программу? Может пусть этим все же единая точа занимается?..
Так же как и не одно десятилетие работаем с ФС, у которых имена с разной нормализацией Unicode, а так же без нормализации Unicode гарантировано означают один и тот же файл. И тоже без «кровькишки» обходимся.
В чём проблема?
Сейчас ссылок не дам, но точно вам говорю: кровькишки регулярно встречаются. Особенно, когда привык к идеологии «что прочитал — то и записал». Возможно, это деформация линуксоидов какая-то :)
Встречаются же 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 — да ;)
linux подсистема Windows является ubuntu и глючит не больше оригинального ubuntu.
Только вот она и не нормализует названия файлов. https://github.com/Microsoft/WSL/issues/1820
А что, например, с FAT эти миллионы строк до сих пор не умеют? И вы не считаете это проблемой?
В Linux — то же самое.
Нет, миллионы строк нерабочего кода и if'чик в инсталляторе — не то же самое.
Ну как прекрасно… Без ничего, с отключенными гибернацией и файлом подкачки? А если ещё и сжатие включить…
Ну, несерьезно.
Раньше вместо симлинков ярлычки были, если помните. Если бы не отказались от FAT — и сейчас бы были. Но да, тоже повод закопать стюардессу.
Нет, конечно, можно в ФС ничего не делать, а требовать от всех приложений что б они все идентично нормализовали имена сами ;) Как это сейчас в Linux и происходит, что и позволяет более менее сносно жить. Но такой подход как раз и является тем самым набором граблей ;)
P.S. А ошибки переполнения буферов в неправильных программах они завсегда были и ещё долго будут, причём, что при работе с «вашими» наборами байт, что при работе с нормальными именами Unicode.
Ну тогда объявляем примерно весь софт под Linux "неправильным" — он ведь опирается на предположение, что читает то же самое, что и пишет.
требовать от всех приложений что б они все идентично нормализовали имена сами
Нет, в случае, когда ФС не лезет не в своё дело, каждая софтина может выбрать свой способ нормализации и он будет работать. Да, мы можем создать два "уй" — файла, но зато мы не требуем от каждого приложения заниматься нормализацией всех UTF-строк, которые потенциально могут оказаться в имени файла.
Каким боком выше перечисленные системы требуют у каждого приложения заниматься нормализацией? А? По какому имени файл создал, по такому же его и открыть можно ж и т.п. (да, его же можно открыть и по другим именам, эквивалентным данному имени, но почти для всё приложений это не проблема ж)
Некоторое понимание в делах независимости имён файлов от регистра символов или нормализации требуется лишь небольшому числу приложений, например, cvs или svn.
(да, его же можно открыть и по другим именам, эквивалентным данному имени, но почти для всё приложений это не проблема ж)
А ещё его можно попытаться поискать в листинге, например. Возникает проблемка, не так ли?
del, не та ветка
NTFS (Windows) поддерживает
Как раз NTFS вообще ничего не поддерживает, кроме
регистронезависимости. Для него имя файла — это UCS-2. Попробовал создать два файла: й.txt и й.txt. Прекрасно создались.
А подход файловых систем Linux — набор байтов, это набор граблей.
Я всегда считал, что Unicode — это набор граблей. Это стандарт, который постоянно дополняется и который невозможно реализовать полностью. Плюс затраты процессорного времени на нормализацию.
Так что подход Linux я считаю более адекватным.
регистронезависимости.
Регистронезависимость это фича 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 фиксили. Он же не только на винде работает.Как прекрасен этот мир, посмотри… поколение «не рефлексирующих, а распространяющих», похоже, окончательно победило. С учётом того, что вас ещё и плюсуют.
Это у вас «не на винде реестр вдруг появился? С ключом
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\kernel\
dword:ObCaseInsensitive?
Это где же вы такое нашли, интересно? И где вы в 2006м году нашли .NET не под Windows?
Нет, всё это точно было в Windows XP, не удивлюсь если и в Windows NT 3.1 тоже всё было — просто проверить негде.
А в 2003 году был всего лишь недокументированный флаг в реестре, который техподдержка включала для каких-то специфических кейсов отдельных пользователей. Ну и код в драйвере. Но там очень много очень разного кода, не все из этого «винда поддерживает»
Надо просто понимать разницу между документированными функциями ОС, и какими-то флажками в реестре.А какая разница? Где сегодня, сейчас, в Windows 10 большая кнопка, которая включает/выключает регистрозависимость? Чем команда в PowerShell «интуитивнее» ключа в реестре?
Так вот в винде, как экосистеме, регистрозависимые имена файлов появились в прошлом году.В винде, как в экосистеме, всё это появилось больше четверти века века назад — в Windows NT 3.1. Где и флажок FILE_FLAG_POSIX_SEMANTICS был (это, блин, вообще первый «расширенный» флаг, который в CreateFile появился) и регистронезависимость была и даже POSIX-подсистема была!
А в 2003 году был всего лишь недокументированный флаг в реестре, который техподдержка включала для каких-то специфических кейсов отдельных пользователей.Вот только этот флаг (и он вроде как действительно появился только в Windows 2000) не включал регистронезависимость, а выключал её.
Но там очень много очень разного кода, не все из этого «винда поддерживает»Конкретно регистронезависимость она поддерживала весьма прилично ибо ей нужно было пройти POSIX-сертификацию. Ибо в те времена в госучреждениях разрешалось использовать только POSIX-совместимые OS.
Сейчас это поддерживается во всех стандартных сценариях, и насколько помню, в RS5 они хотели вообще включать это по дефолту в новых установках.
Флажок, когда появился, позволил оптицонально эту поддержку отключать — отменяя флаг FILE_FLAG_POSIX_SEMANTICS, который, ещё раз повторяю, был там с самой первой версии Windows NT.
Изначально поставляя Windows NT как POSIX-совместимую систему начиная с Windows 2000 Microsoft начал делать попытки заставить всех отказаться от POSIX и перейти исключительно на Win32 API. Извините, но инсталлятор не может «случайно» залезть в реестр и выключить там регистронезависимость. Этот код кто-то должен был в него целенаправленно добавить. Совершенно случайно посреди инсталлятора вполне конкретная строка в 50 символов длиной не возникает.
Но с учётом того, что Microsoft и так в судах трепали в то время изрядно — признать это Microsoft не мог и потому пришлось делать вид, что «ой — да мы ошиблись просто, никто ничего „такого“ и не хотел».
Но окончательный отказ от этих попыток — да случился не так давно.
Как вы это сделали? Расскажите! Интересно!)
bopoh13, Squoworode, domix32, спасибо большое!)
ВОТ ЭТО ПОВОРОТ.
Да, реверс строки сломается если в строке не строка.И?
А еще меня как Сипипишника ввело в недоумение вот это утверждение:
самым оптимальным для себя я долго считал такое решение:
const strReverse = str => str.split('').reverse().join('');
split и join — это эффективно? Эффективнее чем тупо перебрать половину строки и сплитнуть руками с другой половиной? Ладно, нет возможности модифицировать строку. Но что мешает без сплита сразу писать в массив и его конвертировать в строку? Разве это не будет эффективнее?
Я бы охерел, если бы на вакансию С++ программиста кто-то для реверса строки сначала её в отдельный список/массив конвертировал.
Если в вебе это норма — не удивительно что у нас всё так плохо в браузерах.
Если я не прав в своих суждения — расскажите пожалуйста почему автор считает такой подход правильным и наиболее эффективным.
Там еще и эффективными считаются цепочки из операций filter/map над массивами, которые порождают промежуточные массивы.
Если нужен filter/map для итераторов, то ищем реализацию на стороне.
.split().reverse().join()
— это что-то вроде шутки в рамках контекста такой задачи.Удивительно, что автор посчитал этот ответ зачетным.
эффективно по количеству символов в решении? по количеству затраченного времени? по стоимости часа программиста способного это написать?
Так что ваш довод не принимается. Если заменить в моем тексте слово «эффективно» на «оптимально» суть ни на грамм не поменяется.
split и join — это эффективно? Эффективнее чем тупо перебрать половину строки и сплитнуть руками с другой половиной?
Из вашего ответа следует, что вы оцениваете производительность решения. Автор же явно имел ввиду другие критерии. Грубо говоря ваш диалог выглядит так:
Автор: вот короткое решение
Вы: разве это быстрое решение?
В питоне реверс массива выглядит еще короче, что-то типа myarray[::-1], как там «под капотом устроено» нужно смотреть дополнительно.
А вот в c++, данная задача хороший индикатор на некоторую адекватность разработчика. Ну просто не всегда нужно мыслить терминами одного инструмента при использовании другого, тем более они предназначены для разных задач.
Я часто слышу что JS гавно. Но здесь я вижу не проблему языка, а проблему выбора решения внутри языка.
что JS гавно
Смотря с какой точки зрения.
Производительность, потребление памяти, отсутствие нормальной мультипоточности (да я знаю про webworkers) по сравнению с более низкоуровневыми языками — да я тут согласен.
Но, если смотреть на порог вхождения, умение упрощать некоторые оплошности, отсутствие зубодробительных практик из того же С++ (я про указатели, наследование, темплейты, виртуальные деструкторы и т.д.), он почти идеальный кандидат для массового применения в действительно областях, где требуется большое количество разработчиков. На мой взгляд очевидно, что сайтов бизнесу можно куда больше и быстрее написать на javascript, чем на c++.
Что написал — то получил. Любое действие можно разобрать до минимальных шагов, а не спотыкаться. А стандартные библиотеки подняли «низкоуровневость» С++ до «среднеуровнего» благодаря своим алгоритмам и контейнерам.
В то же время, я садясь за питон, чуть волосы на голове не вырвал по началу. Он мне понравился, но на сколько же он сложен. По переменной не понятно что она такое. Каждый объект может иметь внутри произвольные переменные. Нет ни удобных указателей что бы изменить внутри функции значение аргумента, или что бы хотя бы не копировать этот аргумент каждый раз.
Деструкторы, наследование, указатели, вот это все есть либо в неявном виде (нужно понимать как работают эти ссылко-указатели в языке) до явного когда деструктор это метод «Деинит» который закрывает соединение корректно, или еще-что то.
В итоге все тоже самое, только в куче.
Хоть и быстрее писать после изучения, не спорю, но «неопределенность» модных в бизнесе языков сильно повышает порог вхождения в программирование в целом
C++ в разы проще чем весь этот треш типа пизонов перлов джсов и т.д.
Это вопрос привычки. Для того кто всю жизнь писал на С++ кажется что он проще, для того кто писал на javascript чтo javascript.
А возьмите человека который пишет на java или C#, так для него что С++, что javascript это неудобный адовый ад.
удобных указателей что бы изменить внутри функции значение аргумента
Вообще-то объекты передаются по ссылке. Вы легко можете их изменить внутри функции, но ничего хорошего в таком подходе нет.
С++ не проще. Он сложнее. Писать на нем дольше (вы и сами с этим согласны). Да, он даёт бОльший контроль за некоторыми вещами, но этот контроль часто не нужен. Вы же не контролируете из С++ переключение вентилей напрямую? Абстракции — это крайне полезное изобретение человечества.
Как не проще, если С++ предлагает вам почти тот же уровень абстракций, как и «простые» языки, и, если нужно позволяет вам опуститься на уровень ниже. Разве не это простота?
Для того что бы писать на «простом» языке больше чем 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 в таблице на миллион пунктов), то да, его нужно оптимизировать всеми силами и средствами, потому что какая–нибудь мелочь может привести к разницы в полсекунды. Если разницы нет, то и смысла нет — выбирайте, что читаемее.
Медленно работающий гмейл — это результат того, что плохой код в критичном месте, понимаете? Критичные места — это то, что нужно оптимизировать.
Критичные места — это то, что нужно оптимизировать.Если вы прочитаете соответствующую статью, то обнаружите, что… миф таки остался мифом. Судя по тому, что что там осталась куча метрик — критичные места там диагностировались и оптимизировались. Результат — тормоза и дикое потребление памяти.
Медленно работающий гмейл — это результат того, что плохой код в критичном месте, понимаете?Нет — не понимаю. Более того — не понимаю категорически. Я ещё ни разу не видел ситуации, когда «феншуйный» год, который писали думая об «идеоматичности», «гибкости» и прочим разным баззвордам (но не об эффективности) после оптимизации работал бы хотя сравнимо по скорости с кодом, который писали изначально думая-таки об эффективности.
Если у вас есть массив из 4 элементов, и вы по кнопке его сортируете, то сортируйте хоть перебором, 24 перестановки комп переберёт почти так же быстро, как и отсортирует пузырьком. Место не критичное.Рассуждение кажется разумным — но он в корне неверно. Ибо предполагает, что вы знаете заранее что там будет 4 элемента. А их там может оказаться, в результате работы какого-нибудь «дезигнера» и 44 и 444444 — ничего заранее предскзать нельзя, если не контролировать что вы делаете на всех этапах.
Я с этим сталкивался неоднократно: когда меня просят что-то оптимизировать и я разрушаю «весь феншуй» путём превращения кода из «торта Наполеон» в 100500 слоёв в что-то гораздо менее «феншуйное» — то у меня часто спрашивают о том, как я собираюсь это профайлить и откуда у меня будет вылезать статистика… и вы знаете… я обычно вообще не собираюсь об этом думать. Того факта, что версия «не по феншую» работает в 3-5-10 раз быстрее оптимизированной в «критических местах» «феншуйной» версии — обычно оказывается достаточно.
P.S. Это, кстати, не значит, что я совсем никогда не пользуюсь профайлером. Это было бы глупо. Но важно всегда понимать заранее — где у вас в программе неэффективности… тогда профайлер подскажет вам — какие из них реально влияют на код, а какие — нет. Если же вы списка неэффективностей не имеете — можете хоть упрофайлится, результатом будет GMail.
Например, есть задача — отсортировать массив интерфейсов устройства USB при обработке события втыкания этого устройства. Вы туда что, квиксорт бы стали пихать?
И вообще может задача найти первый отсортированный, тогда можно не доводить сортировку до конца, а сделать сортировку кучей которая после первого этапа имеет «наверху» самый большой элемент.
Если смысл именно найти наибольший, то естественно нет смысла. Наиболее разумно либо класть уже в отсортированном порядке либо класть в конец и вызывать сортировку кучей, если предваритель но массив уже был кучей, то установка нового элемента в нужное место кучи будет быстрой. короче это хороший способ если периодически требуется выдавать несколько самых больших/маленьких элементов, переодически вставляя значения.
Исходя из нового описания, без сортировки не обойтись за счет плюса и требования отдавать полностью отсортированный список, а вот если отсортированный список не нужно, а нужно только вернуть записи по критерию, то можно пробежать один раз без сортировки.
Можно в принципе передалать архитектуру, чтобы при подключении/отключени устройства к нам прилетало это событие и мы внутри себя уже хранили бы отсортированный списк, тогда сортировку проводить каждый раз было бы не надо и надо было бы выбрать по критерию и вернуть уже хранимый нами отсортированный список.
Как результат у нас будут эффективно сортироваться как маленькие массивы, так и большие.
После чего этот код переходит Вам и Вам надо с ним работать. При этом времени «это всё выкинуть и переписать с нуля» Вам абсолютно точно не дадут, потому что код работает и приносит бизнесу деньги. Ну как, нравиться перспектива, или Вы бы предпочли, что бы код изначально писался бы пусть и не так быстро, но зато «феншуйно», давая возможность даже средним специалистам найти узкие места и оптимизировать только их?
Начну с того, что у моего руководства есть идея фикс: «сейчас мы наймем новых людей и они сделают все хорошо». Совершенно восхитительная идея, что набивший шишки на предыдущих проектах, опытный работник заведомо хуже любого новичка. Я сам, кстати, так пришел, и был в восторге от возможности разрабатывать архитектуру, делать все так, как хочется, без оглядки на кого бы то ни было. У меня тогда хотя бы лет пять опыта разработки было.
Пару лет назад началась очередная итерация «сделаем все по-новому». Пришли студенты (прям реально студенты, они тогда еще доучивались) и понеслось. Нет, ребята очень умные, вот только опыта никакого. Запилили структуру данных, в которой и десятка тысячи записей никогда не будет. Ни о какой высокой нагрузке речи не идет — по сути это конфиг устройства, обращения к которому достаточно редки. Умудрились впихнуть туда самописные би-деревья и хэш-таблицы. И это не смотря на то, что сам конфиг хранится в JSON, а используемая либа — jansson — сама по себе (сюрприз!) основана на хэш-таблицах. Нет, надо показать, что мы не лыком шиты и знаем алгоритмы.
И такое везде — хитрые операции с указателями, огромные макросы, трюки с gcc. Такие практики имеют право на жизнь, но там, где они необходимы, а не по всему коду, который становится абсолютно нечитаемым.
Самое смешное обнаружилось, когда эта разработка начала внедряться, и с ней пришлось работать остальным. Ребята не сделали НИЧЕГО для синхронизации доступа из разных процессов! После того, как на это им указали, сначала вяло отнекивались, что это должно работать как-то «само по себе», а потом все-таки запилили один big fucking lock на весь конфиг. На предложение сделать ниспадающую блокировку, замораживающую только ветки, растущие из нужной ноды, сказали, что это сложно и долго делать. Вот так: оптимизировали би-деревьями, а потом все процессы ждут, пока один закончит свою работу.
Что имеем в результате. Косую архитектуру, никакую мотивацию у старых работников и постоянное переписывание кода. Зато за это время мы слышали много умных слов про «чистый код» (в реальности весьма кривой), про крутость датамодели (в реальности очень костыльная реализация в JSON) и про оптимизации (в реальности хэш-таблицы на списке из ТРЕХ команд).
Похоже, вы разбираете говнокод посредственных программистов. Это нормально, потому что посредственных больше, чем хороших. Но вы ради интереса разберите хороший проект на javascript. Vue.js, например (можете выбрать любой, какой хотите). Ускорить, удалив 80% возможностей вы, без сомнения, сможете. Но сможете ли ускорить, оставив функционал?
Ускорить, удалив 80% возможностей вы, без сомнения, сможете. Но сможете ли ускорить, оставив функционал?Смотря что понимать под «функционалом». Если мы о бизнес-требованиях — то да, легко. А вот если о метаниях дезигнеров или всякого рода баззвордом — нет, конечно. И не нужно.
Хорошо. То есть если я хочу какие-то дизайнерские особенности, мой дизайнер нарисовал и я хочу заплатить денег, что бы это реализовать, вы не можете и денег не возьмёте. Скажете "не нужно, не делайте так".
А люди, которые делают 20 слоев абстракций — сделают. Что ж, это полностью объясняет, почему у софта, который всё же взяли и сделали, так много слоев, не правда ли?
Более того, это даже в опенсорсе работает. Если люди просят "нам нужна библиотека, которая может делать супер-пупер Х", то есть всего две категории разработчиков. Которые могут сделать быстро, без лишних абстракций, но и без лишнего функционала (потому что "не нужен" и его без слоев просто не получается реализовать) и те, кто реализует весь "ненужный" функционал, библиотека становится популярной, но там много слоев.
Медленно работающий гмейл — это результат того, что плохой код в критичном месте, понимаете? Критичные места — это то, что нужно оптимизировать.
Я думаю, там вопрос не столько в качестве кода, сколько в количестве. Не даром он там 600мб памяти занимает.
Он даёт очень много возможностей по сравнению, например, с аутлуком. Не знаю, на чем написан аутлук, но точно не на JavaScript.
Только не спрашивайте, каких. Просто посмотрите, сколько людей пользуются 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 потребуется…
Сначала пользовались ей иногда, теперь постоянно, т.к. стандартная версия даже на десктопе уже достала тормозить.
Тема от подходов к программированию плавно перешла к управлению продуктами. Tensorflow на самом деле хорош. Облачные продукты у них нормальные. Интерфейс, конечно, шлак и документация постоянно устаревшая, но богатство возможностей поражает. Андроид не плох. Конечно он родился больше чем "5-7 лет назад", но новые версии вполне вменяемые.
Впрочем, вашу идею я понял, менеджерская прослойка загнивает. Отрицать не буду, в каждой корпорации это происходит когда-нибудь, кто-то выживает, кто-то нет.
Он даёт очень много возможностей по сравнению, например, с аутлуком.Я, вообще-то сказал достаточно однозначно: «не даёт ничего по сравнению с тем же GMail'ом десятилетней давности». Когда уже и чаты и всякие прочие плюшки (даже тот самый Google Buzz) уже были — а таких объемов… добра в кода и таких тормозов — не было и в помине.
И я даже знаю какие вещи (в том числе полезные) за это время появились (скажем Priority Inbox) — но я не знаю ни одной из ничего, что нельзя было реализовать в версии той же десятилетней давности, требующей на порядок меньше ресурсов и на порядок более отзывчивой…
function func(str) {
ret = "";
for(i = str.length; i >= 0; i--)
ret += str.substr(i, 1);
return ret;
}
По идее здесь нужен билдер, который будет принимать сразу размер и потом заполнять не меняя размер. но я не знаю как это в 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;
}
Поэтому если вдруг (хз правда зачем) я пишу микросервис на js, который должен реверсить строки в промышленных масштабах, то я возьму более быстрый вариант. В прочих случаях я предпочту более компактный и понятный, потому что даже с ним операция занимает меньше 1 микросекунды.
o += s[i--]
Спасибо, не надо такого.
function reverse_06 (s) {
for (var i = s.length, o = ''; i--; o += s[i]) { }
return o;
}
В проект это совать… Комментов больше чем кода напишешь.
Буквально на днях читал доклад александреску, причем на сишарпе, и там пожалуйста, то же самое… А ведь для кого-то это икона.
function reverse_06 (s) {
var o = '';
for (var i = s.length; i >= 0; i--) {
o += s[i];
};
return o;
}
Тогда уже.
Хотя почти это же написали выше.
Потому что эффективность так же требует критерия как и оптимальность. Реализация может быть эффективна по: тактам процессора, используемой памяти, простоте дальнейшего сопровождения, эффекту производимому на вопрошающего и т.д. и т.п. И вовсе не факт что это будет одна и та же реализация.
- Максимально разжеванным для компилятора с циклами вида
for (let i=arr.length-1; i>=0; --i)
, заранее выделяя все массивы нужной длины, с выносом всего неизменного в константы, явным приведением типов в духе asm.js и хранением всех данных в плоских типизированных массивах. - Модном сейчас функциональном стиле с кучей лямбд, map, созданием десятка промежуточных массивов, хранением данных во вложенных словарях с обращением по строковому идентификатору.
и оказалось, что время их выполнения абсолютно одинаково и примерно равно времени выполнения такого же кода на C.
Относительно кода автора, уже были сделаны бенчмарки, сравнивающие время выполнения обращения строк и выяснилось, что разница примерно в 2 раза по сравнению с обычными циклами: jsperf.com/string-reverse-function-performance (код автора: «in-built functions»). Да, не оптимально, но в рамках допустимого: если это не узкое место в программе, то решение в одну строчку гораздо более читабельное и поддерживаемое. Думаю, если бы не умные оптимизации компилятора, разница была бы раз в десять.
и оказалось, что время их выполнения абсолютно одинаково и примерно равно времени выполнения такого же кода на C.
Надо думать второй вариант будет потреблять больше памяти и чаще дергать сборщик мусора, т.к. функциональный стиль подразумевает иммутабельность aka «создание десятка промежуточных массивов»
«создание десятка промежуточных массивов»
Не обязательно, умные компиляторы/интерпретаторы умеют создавать только изменения или вообще менять in-place при сохранении иммутабельности с точки зрения программиста, т.е. когда это можно делать и выгодно делать.
Я бы охерел, если бы на вакансию С++ программиста кто-то для реверса строки сначала её в отдельный список/массив конвертировал.
А C++ что ли не умеет в итераторы?
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)).
stackoverflow.com/questions/16696632/most-efficient-way-to-concatenate-strings-in-javascript
Но, как я читал, в Java идут разговоры о том, чтобы в следующих версиях улучшить реализацию String так, чтобы конкатенация по дефолту происходила так же быстро, как сейчас через StringBuilder. Так что, вполне вероятно, лет через 10 мы все забудем про конкатенацию строк как дополнительный n )
Я часто работаю со строками (пишу мини-парсеры для сменного контента в мини-играх), поэтому постоянно ищу, где бы улучшить обработку строк — но пока прихожу к выводу, что оптимизацию надо делать отдельной задачей, после того, как реализован проект — и там уже менять то, что дает заметный для пользователя эффект тормозов
Хотелось бы ещё увидеть результат с инициализацией массива через new Array(10000). Без него как-то не интересно.
Ну и для полноты картины:
Ссылка на модифицированный тест
Ага, а реаллокация массива в таком варианте, значит, не происходит?
Тогда уж лучше так?
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;
};
Просто так сплитнуть с другой половиной не выйдет, потому что utf8 же и символ может занимать разное количество байт. Да и те же умлауты, у вас немецкий текст может в кашу превратиться.
Только взятие символа в таком случае имеет сложность 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 разных местах, например.
в ЖС нет концепции итераторов
https://developer.mozilla.org/ru/docs/Web/JavaScript/Guide/Iterators_and_Generators
Смысл итераторов в том, что это по идее zero cost, который развернется оптимальным образом.
Если в вебе это норма — не удивительно что у нас всё так плохо в браузерах.Это близко к норме в вебе.
Если посмотреть на пресловутый «leftpad, ломавший интернет своим отсутствием», там тоже был очень неэффективный (а в некоторых _редких_ случаях было бы совсем плохо) код.
(Добавление по одном пробелу слева вместо того, чтобы сразу добавить нужное количество.
Да, это экономит усилия программиста и помогает избежать "третьей из двух проблем программирования". )
Это всё к тому, что надо знать разные тонкости, а так же иметь банальный IT кругозор чтобы знать, что спросить у гугла. В этом смысле задачка отличная.
где эти миллионы мультиязычных сайтов?В китае и японии:)
Мы до сих пор активно используем однобайтовые кодировки, если объемы текста для обработки большие и язык в однобайтовую помещается. Мало того что php до сих пор не вполне адекватно работает с утф8 (за другие языки не особо знаем, но наверняка нюансы есть), так еще и на скорости и объемах отражается. Небольшое количество альтернативных символов в тексте вполне решается через &что-нибудь там.
PS: мы тоже на однобайтовой сидим и чесно говоря не вижу проблем. 2 языка анг и рус легко поддерживаются, а больше у нас нет и не будет :)
Небольшое количество альтернативных символов в тексте вполне решается через &что-нибудь там.
Из за двубайтных кодировок сжигается больше электроэнергии при хранении и передаче и портится природа… :)А сколько энергии сожглость при передаче русских букв в виде "
éô
" (это только две буквы) и т.д.? ¯\_(ツ)_/¯невозможностью отображать рядом символы разных языков.Распространенное заблуждение у тех, кто не застал до-утфную эпоху:)
Неудобство — да, невозможность — нет. Ибо html entities
Это кривой и неработающий костыль. Простой пример: вам надо посчитать длину строки. Как вы это сделаете? Напишите свою реализацию strlen с поддержкой entities? Другой пример: вам надо отрезать последние 5 символов. А если вам надо сделать поиск регуляркой? Третий пример: а как пользователь введет свое имя, если у вас страница в 8-битной кодировке, а оно у него с арабскими символами? Их нет в вашей кодировке и браузер не может их отправить (кодирование форм с помощью HTML entities не предусмотрено).
Далее, когда вы захотите вставить в одну строку другую, вам надо помнить о том, какие данные в этих строках — сконвертированные в entities или нет, и сконвертировать при необходимости, чтобы например символ & был бы закодирован как & amp ;. и так у вас в программе будет половина строк закодированных. а половина нет, и вы будете делать ошибки или писать типы-обертки для разных видов строк. Плюс, если вы выведете где-то незакодированную строку, возникнет XSS-уязвимость. А понять, есть она у вас или нет, нельзя, так как данные экранируются в самых разных местах кода.
В общем, число граблей тут такое, что разработчики либо будут значительное время тратить на их решение, либо проигнорируют и будут писать кривой код с кучей уязвимостей. Освоить Unicode все же будет выгоднее.
Простой пример: вам надо посчитать длину строки. Как вы это сделаете? Напишите свою реализацию strlen с поддержкой entities?
Подозреваю, что strlen тоже неочень дружит с составными emoji.
Простой пример: вам надо посчитать длину строки. Как вы это сделаете?Наш оппонент писал про "невозможность отображать рядом символы разных языков",.
Простой пример: вам надо посчитать длину строки. Как вы это сделаете? Напишите свою реализацию 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-8 без BOM».
Почему? Чтобы узнать длину символа UTF-8 достаточно прочитать его первый байт.Сорри, не то имели ввиду. Имели ввиду, что даже если у Вас текст полностью в одной из однобайтовых кодировок, то Вы точно знаете что 1 это 1, а вот в утф8 это не так однозначно. Таким образом утф8 непредсказуем с самого начала, а однобайтовые кодировки непредсказуемы только когда есть атипичные символы.
однобайтовые кодировки непредсказуемы только когда есть атипичные символы.
UTF-8 тоже. Просто «атипичными символами» считаются все, после 127-го.
UTF-16 — ещё лучше — «атипичные» только суррогатные пары.
UTF-8 «атипичными символами» считаются все, после 127-го.Это и есть проблема. Утф8 считает атипичными символами вполне типичные символы типичного сайта, типично сделанного на одном типичном языке. Из-за чего даже банальный природно-однобайтовый сайт даже тупо на немецком/русском/французском вдруг оказывается с точки зрения утф8 атипичным.
А учитывая, что «атипичные» символы что в UTF-8, что в UTF-16 занимают по 2 байта, то никакой проблемы здесь нет.
Это не проблема, если вспомнить, что большую часть даннх составляёт HTML/CSS/JS, а не собственно текст на человеческом языке.В БД все же текст, и именно с текстом проводятся все операции — поиск, обрезание, редактирование, анализ.
Но даже если говорить не про хранение, а про распространение — то разница все равно есть и к тому же хотелось бы надеятся, что html не занимает бОльшую часть данных.
«атипичные» символы что в UTF-8, что в UTF-16 занимают по 2 байта, то никакой проблемы здесь нет.Так речь шла о сравнении однобайтовой национальной с юникодом.
немецком/русском/французскомМне больше интересно куда вы свой немецко-русско-французский текст с однобайтовой кодировкой собрались засовывать…
Не вебом единым.
Неудобство — да, невозможность — нет. Ибо html entitiesЯ в прошлом году занимался интеграцией нашей компании с одним крупным европейским производителем (2 место в мире по производству, годовой оборот 20млрд долларов).
Они используют в своем api для текстовых сущностей не UTF, а html entities
Так даже сам 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 кодпоинта).
Символы, кодируемые суррогатными парами — далеко не только эмодзи.Совершенно верно. И именно поэтому возникает вопрос: с какого перепугу мы начали, вдруг, заботится, о «разноцветных» смайликах, но «забили» на диакритику, арабику, корейцев и прочее.
Так-то задача «разверните строку, состоящую из кодпоинтов» — вполне корректна… только она не имеет ничего общего с обсуждаемой статьёй.
Акела промахнулся del
const strReverse = str=>{
let result = [];
for(let a of str){
result.unshift(a);
}
return result.join('');
}
Получилось коряво, но я не успеваю отредактировать в нормальный вид.
Случаи с декомпозицией — отдельная штука, тут нужно писать функции для нормализации юникода.
Какой классный язык, в котором нельзя быть уверенным даже в примитивный функции split() (по словам автора)
Про комбинационную диакритику вы уже всё поняли (кстати, привет эппловцам! некоторые приложения на маке вместо и-краткого делают и+кратка, повбывавбы!!!)
Следующий челлендж — это корректная работа с справа-налево и двусторонним текстом.
И вот тут я хз, какое должно быть ТЗ. То есть — как оно вообще правильно?
В случае с комбинационными символами (где каждому глифу соответствует цепочка кодов) всё просто: нарезаем на цепочки и переставляем их местами, сохраняя внутренний порядок.
А вот в случае с биди… тупое перемещение управляющих кодов расколбасит текст так, что он визуально совсем не будет похож на перестановку глифов на экране.
Oh wait… Самый простой способ перевернуть строку — это добавить управляющие коды биди!!!
Единственно, что такой переворот будет необратимым. Reverse(Reverse(s)) != s
.
Опаньки… А ведь в ТЗ про это не говорилось. Любишь подразумевать — люби и расхлёбывать.
Ах да, на сладкое. Есть такие штуки, как лигатуры. Помимо того, что некоторые лигатуры имеют свои собственные коды (например, œ), а некоторые не имеют (например, ij), — так ещё и от шрифтов зависит, будет ли пара символов f+i отображаться одним глифом или двумя.
Поэтому — следует ли слово "fifi" перевернуть в "i f i f" или в "fi fi" ?
Ну и совсем на сладкое. Загадка для любителей переворота строки.
Очевидно, что предикат IsPalindrome(s) = (s == Reverse(s))
.
Вопрос: корректно ли он сработает на строке "(((а роза упала на лапу азора)))"
?
Любишь подразумевать — люби и расхлёбывать.вот это, с вашего позволения, я в свой блокнотик утащу, пригодится
Вопрос: корректно ли он сработает на строке "(((а роза упала на лапу азора)))" ?
Не совсем, косяки с пробелами — ароза упал ан алапу азор а
еще скобки
Скобки — это самое главное заподло в этой строчке ;)
Задача же стоит просто строку развернуть. Задание выполнено. А функция палиндрома вернёт ошибку даже без скобок, из-за косяков с пробелами.
Если будет отдельно сказано сохранить функцию скобок — круглых, квадратных, фигурных и угловых — тогда будем работать над этим
Так в этом и фокус. Цикл разработки:
- Написал функцию проверки на палиндромность.
- Написал юниттесты на чётное и нечётное количество символов, на пустые строки, всё работает, доволен как слон.
- Отдал тестировщику, он скормил типичный палиндром про лапу Азора.
Недолго думал, сделал пробело- и регистронезависимую реализацию, добавил Азора в юниттесты, всё работает, доволен как слон. - Отдал тестировщику, он скормил палиндром с кавычками:
«А роза упала на лапу Азора»
(скопипастил из своего чеклиста в ворде).
Долго думал, сломал глаза, понял, пошёл ругаться с тестировщиком и архитектором на предмет ТЗ.
Скобками можно троллить соискателей:
Является ли палиндромом строка
((((
(())
Кстати о биди. Алгоритм рендеринга биди-текста должен уметь самостоятельно разворачивать скобки в нужную сторону!
(А умеет ли он разворачивать кавычки с учётом национальной типографики — это хороший вопрос… Русские и английские лапки несовместимы, и это типичная ошибка при дизайне шрифта — вместо верхней bb-лапки сделать верхнюю pp-лапку, а потом русский курсив будет пускать кровь из глаз).
Судя по комментариям, я один считаю автора снобом?
Начнем с первого вопроса. Буду говорить о 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, отрубит нам РКН интернет или еще какой катаклизм случится. Как этот молодой и талантливый специалист будет проверять что ему бекенд вернул?
Сори, конечно, за откровенность. Но в случае отрубания интернета РКНом «проверить, что там вернул бекенд» в списке приоритетов будет занимать исчислимую на пальцах позицию снизу списка (сначала проблему доступа к репозиториям решите, например). Не говоря уж о катаклизме, в случае которого этой проверке в списке приоритетов места не найдется совсем.
Но зачем строку-то переворачивать? Это реально нужно в каком-нибудь проекте? Вы на вашей работе хоть раз переворачивали строку в промышленном коде?
var d = Object.create(null, {'d': {value: 42, writable: true}})
typeof d === 'object'
isObject(d) === false
isObject — нет
isObjectLike — нет
isPlainObject — да
PS: А еще меня забавляет ирония ситуации, в которой в ветке комментов два (скорее всего хороших) программиста пишут функции isObject() и isPOJO(), которые возвращают значения, совсем не обязательно соответствующие своему названию.
По мне, если соискатель ответит на вопрос «нет ли подвоха в этой функции», даже без примера, то это прекрасно.
Ну, вот, например, полезное применение. Позволяет использовать POJO в качестве ключей по значению, а не по ссылке.
Если копать вглубь, в JS вы всегда дойдете до duck typing — ну и гляньте, что вы проверяете в вашем методе с точки зрения duck typing. Что у него конструктор определенного вида есть? А занафига мне это для POJO? Когда я вижу «POJO» в яваскрипте, я ожидаю, что у меня есть возможность читать и писать свойства — и как вы это проверили в вашем методе? Да никак. Итого у меня есть
var veryMuchPOJO = Object.create(null);
в который я легко могу писать и читать свойства, а вы своей функцией мне говорите «false».
Ваша функция точно не будет вызывать вопросов, если она будет
function surelyIsAPOJO()
Тут по названию видно, что могут быть ложноотрицательные срабатывания ;)
Object.create(null)
Это не POJO. POJO создаётся через литерал.
Давайте так: с точки зрения спеки у нас нет типа «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 — это когда через литерал».
typeof null === 'object'!— Это официальный признанный баг языка, оставленный для совместимости. Хотя, null, на самом деле, это отдельный тип данных.
Всё, я могу претендовать на зарплату сеньёра?)
Я, безусловно, держу в голове закладку на тему «в js надо аккуратно с undefined и null», но помнить наизусть все детали (в том числе что там возвращает typeof null) — голова треснет. Это такая вещь, которую я могу в консоли проверить в любой момент. Мне достаточно помнить, что особенности есть, но не вижу смысла зубрить конкретику.
Как бы вы отнеслись к ответу «не помню точных деталей, но если мы обрабатываем произвольное значение заведомо неизвестного типа, то лучше на null и undefined проверить отдельно в самом начале — наверняка у нас какая-то отдельная логика для этого случая должна быть»?
Как проверит, что другой молодой и талантливый не передал в его функцию невалидное значение?
Напишет unit тесты и увидит, что для 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 году.
Не ходите в проекты/организации, где разработчикам требуется «typeof null»
Нервы с годового бонуса назад не откупишь.
Ну наконец то найден вопрос которым сразу можно отличить джуна от сеньора! (нет)
> Вот не будет завтра lodash, отрубит нам РКН интернет или еще какой катаклизм случится.
РКН отрубит доступ разрабов к их жестким дискам что ли? Просто не могу себе представить такого сценария, что lodash там или jquery хоп и пропал. Разьве, что по электромагнитной бомбе на все города сбросить. Но в этом случае, проблема отсутствия lodash вряд ли будет кого-то волновать.
> Что касается сеньеров — еще более субъективно, мне кажется, что сеньеру было бы интересно знать, почему это работает именно так, а не как-то иначе, вот и все.
Кому-то это интересно, кому-то другое, а таких нюансов в каждом языке, фрейморке, БД, протоколе милллион. Всего не изучишь, да и смысла нет, тем более что все это меняется каждые два года. Специфика работы у всех разная. Справшивать, не понимая, чем человек вообще раньше занимался не правильно. Вот например начинаешь спрашивать человека по SQL и он валиться на group by having. Значит ли это что это джун? Нет, не значит, надо для начала спросить, с какими БД он вообще работал или в чем заключалась его работа.
Надо спрашивать не что человек не знает и не умеет, потому что таких вещей бесконечное множество, а что он знает и умеет.
И почему бы не спросить что-то из того что вы используете на работе? Неужели, основная ваша работа заключается в переворачивании сторк? Наймете человека, будет сидеть по 8 часов и переворачивать строки. Почему бы не спросить что-то более приближенное к практике?
мой любимый вопрос это: «чему равен typeof null?»
Это нечестный вопрос. Пригождается очень редко. Забывает очень быстро. Код, который полагается на такое поведение, должен комментироваться. Я забуду через неделю, что возвращает typeof null и мне за это не стыдно. Я не могу каждые полгода перечитывать спецификации Javascript только для того, чтобы быть способным ответить на этот вопрос или для того, чтобы приготовиться к случаю (цитата) «Вот не будет завтра lodash, отрубит нам РКН интернет или еще какой катаклизм случится».
Вообще, завалить любого человека на edge-кейсах очень просто. Даже если он реально сильный разработчик с богатым опытом.
У меня вот любимые области, с которыми я много работаю и о которых много читаю, — это особенности HTTP протокола и безопасность в вебе. Это огромные темы, на которые написано много материала и в которых происходят постоянные изменения. Браузеры/сервера постоянно меняются. И эти темы имеют непосредственное отношение к веб-разработке.
Я уверен, вам было бы неприятно, если бы я начал собеседование с заковыристых вопросов по хеадерам, потом скажу, что это вот используется в наших проектах, и раз вы не знаете ответ на этот вопрос, то возможно вы даже до джуниора не дотягиваете.
Что может быть лучше чем компания намеренно полагающаяся на обратную совместимость древних багов в интерпретаторе.
Во-первых, до использования typeof можно явно проверить на null. Прям через оператор сравнения. Это будет явно и очевидно для всех читающих.
Во-вторых, я такие вещи обязательно покрываю unit тестами. Если есть какое-то не очевидное поведение JS, о которым я забыл, я об этом сразу узнаю. Уж на null в тестах проверить можно.
Возможно, есть специфичные проекты, где проверка typeof null — это нечто обыденное, что встречается часто, и без знания этого с проектом просто нельзя работать. Но я в таких проектах пока не работал (что, конечно, не значит, что их нет). Но даже там при ревью тебе коллеги напомнят/подскажут об этой вещи, если эта такой частый у них кейс.
Отдельно Вам доставит (скорее всего, проблем) попытка поработать с null как с объектом
a = {}.field //undefined
b = null.field //TypeError: null has no properties
c = null
d = c.field //TypeError: c is null
И даже если он применяется для проверки на строку и число, он всё равно может делать не то, что от него ждут (впрочем, этот случай уже куда менее вероятен).
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?»
Прекрасная иллюстрация полезности статической типизации
Если в переменной объектного типа находится нулл, то это значит отсутствие ссылки на объект.
В обычной ситуации "под капотом" в переменных объектного типа находятся не сами объекты, а ссылки на них. Нулл — отсутствие ссылки. Присваивание нулла значит "теперь ссылка в этой переменной никуда не ведет".
В JS же, судя по всему, применен паттерн Null object.
Значение null означает, что переменная имеет объектный тип и нуллное значение.
Что куда встает?
Судя по статье, в JS сеньорам приходится запоминать табличку исключений и помнить, что "есть особенности". Рантайм съест нулл и не подавится.
В этом смысле идеальны языки со строгой типизацией, где компилятор будет тыкать носом разработчика еще на этапе написания кода. Вместо того, чтобы помнить табличку исключений, разработчику нужно думать: всегда ли тут существует значение? Что делать, если оно отсутствует?
Значит статически типизированные языки не такие уж и статические? :)
Статические, но недостаточно строгие.
Почему не тыкает? Код выполняться не будет.
Потому что типизация статическая, но не строгая.
Так то и в scala со строгой типизацией тоже можно NPE поймать, но язык задуман так, чтобы все источники null завернуть в Optional и вывести на периферию I/O. В бизнес-логике нуллов вообще быть не должно. Там, где значение необязательно, используется Optional.
Вот и получается разница: в js/java у вас может что-угодно где-угодно оказаться нуллом и вы не обязаны писать обработку этих случаев.
В scala все что у вас может оказаться null, у вас Optional и вас компилятор заставляет учитывать такие случаи.
А вот и приходится думать, потому что компилятор с null не хочет помогать:
Да вроде хочет:
Получается, что язык вроде бы статически типизирован, но я тоже не могу доверять этой типизации и должен у себя в функции проверять someObject на null?
Все так. Java вроде статически типизирована, но при этом типизация недостаточно строгая, чтобы компилятором выявлять проблемы с null.
Null object — это совсем другое.
Я все таки уверен, что то же самое. Буду признателен, если вы объясните почему я заблуждаюсь.
При этом языки заявляются как строго статически типизированные. Не является ли это введением в заблуждение?
Если под "этими" языками понимать java и c#, то они типизированы статически, но не так уж строго. Есть куда стремиться.
Строгость не бинарна. Грубо говоря, это количество правил, в которые компилятор будет вас тыкать носом. Чем больше правил, тем строже.
У java/C# компилятор просто сильно строже, чем в js.
У scala компилятор еще строже, чем в java.
Тем что даже в java и c# null это полное отсутствие значения, противопоставляя тому что в js это значение "ничего".
Тем что даже в java и c# null это полное отсутствие значения
но не отсутствие типа переменной.
противопоставляя тому что в js это значение «ничего».
А вот тут Вы ошибаетесь. Никакого противопоставления тут нет. А логика в JS точно такая же: null — это отсутствие значения, но не отсутствие типа переменной.
typeof null
при этом отвечает
object
Null — это спецзначение объектного типа (так же, как в других языках). Логично, что оператор, возвращающий тип данных переменной, в случае
let v = null;
typeof v
возвращает «object» — переменной присвоен объектный тип, но не присвоено никакой ссылки.
Применение же typeof к константе — это гнусное извращение, ни один язык не обязан с таким работать.
Null — это спецзначение объектного типа (так же, как в других языках).
Далеко не во всех языках это так. Он вполне может быть отдельным типом. Например:
x=None
print(type(x))
//выведет <type 'NoneType'>
var x = null;
print(x.runtimeType);
//выведет Null
Спец значением он является в таких языках как Java, C# или C++. Впрочем, в C++, nullptr является специальным значением указателя, и к объектам отношения не имеет, т.к. указатель может быть на любой тип. Вероятно, Java позаимствовала семантику null из плюсов, и натянула на свою объектную модель, в результате чего он и стал специальным значением объектного типа.
Впрочем, в C++, nullptr является специальным значением указателя, и к объектам отношения не имеет, т.к. указатель может быть на любой тип
Это зависит от реализации. В общем случае это std::nullptr_t
, который может быть чем угодно.
так же, как в других языках
Ну-ну. Напишите в C++
auto v = nullptr;
и посмотрите, какого типа будет v.
Ну или в C#:
var v = null;
будет ещё интереснее.
достаточно ли вы умны чтобы работать в google
P.S. Ну я понимаю, когда просят написать генератор паролей юзеров со встроенной мнемотехникой или сломать что-нибудь. А это и правда немного странно.
Я примерно так отвечаю:
"\u202E" + source
и всё. Правда я не JS разработчик.
https://www.fileformat.info/info/unicode/char/202e/index.htm
Ради расширения кругозора: Swift один из первых (известных мне) языков, кто решился уйти от UTF-16 с его суррогатными парами и по умолчанию работает с юникодной строкой как с набором глифов. Из-за чего привычные способы работы с моноширинными строками у него не работают. Но задачка из топика решается тривиально (String("...".reversed())
).
> а возвращает эту строку «задом наперед»
мы считаем что спрашиваемый умеет чтитать мысли и сам догадается что
— писать надо на каком то дибильном языке програмирования
— у аффтора вопроса свое болезненное понимание «оптимальности»
— хотя в вопросе вооще ничего нет про оптимальность
— но видимо обычный цикл for (длина строки..) — неправильный ответ
— рекурсивный алгоритм видимо тоже неправилен, (думаю аффтор наверно не понимает такого)
— ОППА сюрприз — аффтор считает что вызов reverse() это Нормально!
— аффтор болезненно влюблен в «const»
(давайте может просто спросим про гольфовые мячики в скулбасе?)
Регулярка не понадобилась.
Math.random()
Есть понятие security through obscurity, а это, видимо, пример workability through obscurity?
В вашей крови опасно низкая концентрация хардкора, коллеги.
(хардкор ниже умеет й и ё, но ломается на эмоджах, как старпер этим даже немножечко горжусь)
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")
Оставшийся 1% приходится, в основном, на случаи когда разворачивание строки и есть весь проект, не имеющий, к тому же, коммерческой ценности.
Но поскольку с самого начала речь идет о задачке для собеседований, я полагаю что принципы промышленного программирования можно пока оставить в стороне.
Если бы все таки дошло до разворачивания строки вручную, и человек начал бы писать что-то подобное, многое зависило бы от того насколько этот кадавр работает.
Если он не работает — претендент амбициозен, но переоценивает свои силы, можно брать на позицию джуна, если есть кому плотно за ним присматривать.
Если работает — либо он уже писал именно это в последние пару дней, либо мне попался очень головастый чувак. Пусть и оторванный от действительности. Возможно у меня есть задачи именно для него, тогда он мне ценен. В любом случае, даже если мне он не подходит, я приложу все усилия чтобы донести до него истинную причину отсутствия оффера.
Я, естественно, эту дичь писал, отлаживая ее в браузерной консоли по кускам.
Так тоже можно, конечно, но мне казалось речь в статье немного о другом.
Нет идеального способа декомпозировать задачи, поддерживать стиль, обрабатывать ошибки, именовать сущности, документировать, и так далее, так далее, так далее. Процентов 80, по моим личным ощущениям, все в специфике конкретного проекта, лишь на оставшиеся 20 некие прописные истины вроде «функция не должна занимать больше пол-экрана» и «весь пользовательский ввод должен санироваться» и «не должно быть больше трех вложенных друг в друга циклов».
На собеседовании проекта нет, соответственно нет всех этих 80% требований, который на всех предыдущих проектах в которых человек участвовал, могли радикально различаться.
А требовать ловить все возможные null pointer'ы в функции из пяти строчек, про которую автор точно знает, что ни в какой продакшен она не пойдет, и я знаю что он это знает, это странно.
Вопрос вида «Этот код предполагает уйти в прод, какие вы видите в нем проблемы? Выпустите вы, лично, его в прод, или нет?» я лучше задам отдельно.
Разворот строки это отличная задача для собеседования, она интересна тем что для успешного решения собеседующему даже не нужно писать код. Точнее тот кто сразу бросится писать код успешно провалит эту задачку и сразу будет видно что перед тобой джуниор. Когда же сеньор-разработчик вместо того чтобы начать писать код сразу задаст кучу уточняющих вопросов — что такое строка? Это список двух-байтных символов в представлении js или же это список код-символов юникода или что-то посложнее (графем и т.д)? И зависимости от ответа сеньор будет задавать уточняющие вопросы — а входит ли в понятие элементов строки всякие управляющие символы или символы из разных категорий и т.д.
И я думаю по похожему принципу можно составить кучу других задач которые смогут точно определить кем является собеседующий — джуниором который нахватался отдельных знаний (или натаскал себя на решения типичных задач для собеседовании или даже заучил как студент перед экзаменом без понимания что к чему) или сеньор у которого в голове знания разложены по полочкам и составляют единую картину
Когда же сеньор-разработчик вместо того чтобы начать писать код сразу задаст кучу уточняющих вопросов
Уточняющие вопросы? Их есть у меня!
Это даже не поможет вам пройти интервью в другую компанию, так как код-то в результате написан не будет…
P..S. У нас, например, есть в отчёте об интервью отдельный раздел, куда нужно вставлять код, написанный кандидатом. И если его там нет… то это сокращает время работы всех, кроме собственно интервьюеров, почти до нуля. А уж если там пометить, чтобы его в чёрный список внесли и в будущем на интервью не приглашали — то экономия ещё больше (но тут одного такого интервью не хватит). Так что для фирмы такой подход неплох — но вопрос «а зачем интервьюируемый вообще на всё это дело время тратит» остаётся…
Выглядит намного проще чем через regexp.
Пример: gist.github.com/3AHAT0P/900d7635c16974f73455f94d69252134
Я обычно даю какую-нибудь задачку на то, с чем кандидат будет сталкиваться довольно часто, типа такой (я работаю с 3д-графикой поэтому тригонометрия крайне важна):
Она имеет как минимум 4 хороших варианта решения и если кандидат минут за 10 находит любое — голова варит, можно брать: нюансам синтаксиса научится, а вот пространственное мышление прокачивать нужно гораздо дольше.
Вы не сможете решить эту задачу на собеседовании