Разбираем WTF задачки в JavaScript

Автор оригинала: Mahdhi Rezvi
  • Перевод


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


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


Задачка 1 — «BANANA»



Этот твит в своё время приобрёл определённую популярность в англоязычном сегменте твиттера, посвящённом JavaScript. Не в последнюю очередь потому, что совсем неочевидно, что здесь происходит.


Решение

«b» и «a» в начале — это просто строки, результатом сложения которых будет «ba». После «а» вы видите два знака плюс (+). Первый из них предназначен для сложения, зато второй — унарный оператор, который преобразует следующую за ним строку в число. Поскольку вторая «а» не может быть преобразована в число, она преобразуется в «NaN».


Таким образом имеем: «ba» + «NaN» + «a» — «baNaNa». toLowerCase коварно выдаёт нам конечный результат — «banana».



Задачка 2 — Сложение массивов



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


Решение

Сначала массивы преобразуются в строки — «1,2,3» и «4,5,6». Результатом конкатенации этих строк и будет конечный ответ — строка «1,2,34,5,6».



Задачка 3 — функция parseInt( )


Из всех задачек по JavaScript именно эта для меня оказалась самой сложной.



Если с parseInt в JavaScript так или иначе сталкивались, наверное, все, то о существовании второго опционального параметра «radix» знает уже меньшее количество людей. Это основание системы счисления первого аргумента.


Решение

Первый аргумент «null» конвертируется в строку. Первый символ этой строки «n» конвертируется в число в системе счисления с основанием 24. Результат этого преобразования — 23.


parseInt продолжает парсить строку посимвольно, пока не столкнётся с символом, который преобразовать в число не сможет. Это второй символ строки — «u», так как такой цифры нет в системе счисления с основанием 24.


Таким образом:


parseInt(null, 24) === parseInt("null", 24) 
parseInt("null", 24) === parseInt("n", 24) 
parseInt("n", 24) === 23


Задачка 4 — точность чисел с плавающей точкой



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


Решение
console.log(0.1 + 0.2); // 0.30000000000000004

Константы «0.2» и «0.3» в программе будут аппроксимированы до их «настоящих» значений в машинном представлении. И так уж получилось, что ближайшее double до «0.2» больше, чем рациональное число «0.2». С другой стороны, ближайшее double до «0.3» меньше рационального «0.3».


Таким образом, сумма «0.1» и «0.2» в коде оказывается больше рационального «0.3», а константа «0.3» в коде меньше рационального «0.3».


Хозяйке на заметку: никогда не используйте "===" или "==" для работы с числами с плавающей точкой. Вместо этого используйте


if (abs(x - y) < toleranceValue) { ... }




Заключение


Да, при работе с JavaScript возникает немало WTF моментов. С другой стороны, какой ещё язык может подарить столько незабываемых ощущений?


Ниже список ссылок (на английском языке) для углублённого изучения задачек из этого поста (и не только):


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

Подробнее
Реклама

Комментарии 108

    +23
    Последняя задачка это не WTF JS, а аж целый стандарт IEEE 754, встречающийся не только в JS, но и, например, в Python
      +8
        +2
        Нам, если не путаю, в институте на курсе фортрана преподавали, что вещественные числа сравнивать через оператор равенства некошерно. По мне главный вопрос, почему в языках программирования по сей день нет штатного оператора для сравнения с заданной точностью.
        • НЛО прилетело и опубликовало эту надпись здесь
            +1
            1. если грубо то да. Проблемы начинаются ещё на этапе преобразования десятичных чисел в двоичные. Например 0.2 + 0.2 === 0.4 таки возвращает true.
            2. равенство для большинства практических задач с вещественными числами равенство должно быть приблизительное, но используется как раз точное равенство.
            3. мне сравнения с эпсилон в в долях от целого вполне хватило бы ). Либо явного запрета на сравнение оператором ==, да хоть вы warning показывали.
              +1
              Наверное потому, что сравнивать на равенство вещественные числа нет смысла. Потому что многие из них нельзя точно «оцифровать». Например, 1/3 или π.

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


              Правда, что интересно, сравнение двух вычислимых чисел в общем случае неразрешимо в смысле Тьюринга.


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

              0
              В шарпе, например, штатно присутствует тип decimal, не совсем то о чём Вы говорите, но таких сюрпризов не выбрасывает.
                0
                Это тип с фиксированной точностью, прежде всего для финансовых расчётов и подобного. Хотя и для них может потребоваться сравнение с заданной точностью.
                  0
                  Это, это прежде всего, тип для представления именно десятичных значений. Т.е. 0,2 это именно 0.2 так же как 0.3, это именно 0.3.
                  Т.е. bool eq = 0.2m + 0.3m == 0.5m;
                  eq будет true
                  Но ошибок округления конечно и здесь не избежать, но они хотя бы понятны и логичны даже для школьной программы.
                  decimal a = 1m / 3m;
                  bool eq = a * 3m == 1m;
                  Здесь eq уже false и даже простой старшеклассник далёкий от программирования скажет, ну конечно, ведь a * 3 будет 0.99999999
                  Хотя, стандартный калькулятор у меня в телефоне (андроид) и на винде говорит, что а * 3 всё же будет 1. Так что, не всё ещё потеряно.
                    0
                    Калькуляторы используют небольшой трюк: не показывают последние две-три цифры результата, округляя число при отображении. Если вычислить (1/3-0.333)*3, результат уже будет состоять из девяток.
                      0
                      Больше похоже, что калькулятор где-то в памяти держит не результат 1/3, а именно действие. И тогда у него всё сходится 1/3*3 = 1 пусть я и делаю это в два действия.
                      Потому что если мы разделив 1/3 скопируем значение в буфер, сделаем сброс в калькуляторе, вставим значение обратно и умножим на 3, то уже получится ноль с девятками.
                0

                Вопрос — почему такого оператора нет в Фортране, который специально для математических расчётов создавался.
                В мейнстримных языках такого оператора нет по простой причине — они создавались не под работу с плавающей точкой. А в NumPy, R, Julia — есть стандартные процедуры для проверки "примерного равенства".
                Впрочем, если нужно "равенство" чисел с плавающей точкой — это уже сигнал того, что они, возможно, используются не по назначению.

                  +1

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


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


                  А тернарные операции плохо ложатся в инфиксную форму. Проще оформить в виде функции или бойлерплейтной формулы (abs(x-y)<eps — и так стопятьсот раз в коде).


                  Что до утверждения "если используется равенство флотов..." — жизнь всякие обстоятельства подбрасывает. Можно проверять, если гарантировать, что числа получены одним и тем же способом. Например, что какое-то кешированное значение не изменилось; или что число было прочитано из конфига (и там никакой отморозок не догадался напихать 11 нулей после запятой).

                    0
                    Ладно с ним с Фортраном, язык в лохматом году и, вроде, для прикладных математиков писался, они, в норме, в курсе этой особенности.

                    Но в тот же js можно фукнцию вроде `Math.floatCompareWithEpsilon(a, b, eps)` в стандарт включить. Глянул, в питоне тоже стандартного способа сравнения нет. В ruby нет, в java и C тоже нет. При этом в языках зачастую есть тернарный условный оператор, который хорош настолько, что им рекомендуют не пользоваться.

                    Моя фантазия подсказывает что нибудь вроде: `a ~~ (b, eps)`. И реализацию типа `abs(a — b) < max(abs(a), abs(b)) * eps`.
                      0

                      Что не так с тернарным оператором в С/С++, что им не рекомендуется пользоваться?
                      Кто не рекомендует?

                        0
                        Всё так, пока у нас один такой оператор в выражении, и его операнды компактные. Как только операнды большие, оператор становится нечитабельным. Про вложенность вообще молчу.
                          0

                          А if, значит, с большими операндами — читабельным остаётся?
                          Аналогично ведь и про него можно сказать.

                            0
                            Многострочность, скобки, отступы (табами) и выравнивание (пробелами) никто не отменял.
                          0

                          У Фортрана есть стандарты Fortran77, Fortran90, Fortran95, Fortran2003, Fortran2007 и актуальный Fortran2018. За столько ревизий можно было и добавить, для неофитов.


                          В js как раз не надо, пусть страдают.

                        0
                        Ну фортран вообще прикольный был. Поэлементные операции с массивами, оператор вывода сразу таблицы с зубодробительным (как мне тогда казалось) синтаксисом. Плюс привязка синтаксиса к перфокартам.

                        А в NumPy, R, Julia — есть стандартные процедуры для проверки «примерного равенства».

                        Можно попросить вас поделиться синтаксисом?
                          +1

                          Numpy: numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False) (скаляр -> скаляр, массив -> массив), numpy.allclose() (массив -> скаляр)
                          R: almost.equal(x, y), all.equal(x, y)
                          Julia: x ≈ y, isapprox(x, y; rtol, atol) (x .≈ y или isapprox.(x, y; rtol, atol) для векторизованной версии)

                        +1
                        0
                        Ой, в питоне я с этим столкнулся, второй раз в жизни что-то попытавшись на нем написать. Вот в этой статье habr.com/ru/post/442602 у меня были значения времени светофора с точностью до секунды, а при расчетах скорости и времени машин в пути получались уже дробные значения. Помучился и тупо объявил float вообще везде.
                        +3
                          +11

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

                            +3
                            Почему именно 25 лет?
                            А раньше было всё нормально? Что произошло 25 лет назад?
                              +7
                              Может он стал программистом 25 лет назад?
                                0
                                именно на это я и намекал
                                +1

                                Примерно 25 лет назад я первый раз попытался сравнить два значения типа real на равенство, облажался и узнал у препода, почему так нельзя. ;)

                                  0
                                  Тогда неверно.
                                  ошибки в числах с плавающей запятой знакомы уже почитай 5 лет.
                                    0

                                    Юпитерианских?

                              +16
                              Принято говорить, что С++ сложный, муторный, памятью не управляет, и вообще ад. А я тут смотрю на всякие приколы динамической типизации, преобразования null и NaN в строки без explicit-каста, массивы, которые кастятся в строки с дефолтным разделителем и суммируются конкатенацией, и думаю, что моя судьба С++-программиста не так уж и плоха =3
                                –8
                                Вообще-то, преобразование явно потребовалось, когда значение было передано на "+" (складываются только строки или числа) или другое место, требующее вполне определённый примитив. JS автоматически переводит в нужный тип, и это довольно удобно. Но если передавать чушь, то и на выходе будет чушь. Garbage in — garbage out. Это проблема не языка, а прокладки между креслом и монитором. Все перечисленные моменты описываются в любом учебнике почти сразу же. Удивить это может только человека, не последовавшего мудрому совету RTFM.
                                  +13
                                  RTFM — полезно, да. Однако, любому программисту периодически приходится болеть — спьяну на работу пришел, кранчил две недели подряд, дома ребенок спать не дает =3 В таких ситуациях своевременная индикация ошибок — дело крайне полезное. А тут все это не то, что на этапе компиляции, даже в рантайме не валится. Вот это, гхм, моя основная претензия к JS, да и ко всем языкам динамической типизации, по сути.
                                    –3

                                    Лол, кто-то заехал ко мне в карму и снял единичку. Джаваскриптофаги негодуют.

                                      +1
                                      TypeScript спешит на помощь! ;)
                                        +3
                                        Пересадил людей на Typescript
                                        Они используют :any
                                        Заголовок спойлера
                                        image
                                        –2
                                        Если вы пишете код в таком состояние, то, боюсь, приведение типов будет наименьшим из проблем. Надо проверять правильную работу программы, а приведение — лишь небольшой аспект работы. Тесты что-ли не пишете? Если вы проверяете работу, то ошибки найдёте так и так. Причём все, а не только жалкие 5% связанные с типизацией. Радуют меня такие деятели, которые ставят TypeScript вместо реального тестирования, трятя на типизацию даже больше времени, и думают, что у них всё хорошо.
                                          +2
                                          Гхм, все читаю свое последнее сообщение, пытаясь понять, где ж я сказал, что тесты не нужны. Не получается.

                                          К слову, 100% покрытия тестами не бывает.
                                            +5
                                            Если вы проверяете работу, то ошибки найдёте так и так.


                                            Это да. Но все же предпочтительнее, когда используемый инструмент упрощает эту задачу, а не усложняет, как JS.
                                              –4
                                              Как инструмент, замедляющий работу в два раза, может упрощать задачу?
                                              Когда надо танцевать с бубном, чтобы скомпилировалась уже работающая программа? Который вообще не понимает логику работы. Серьёзно?
                                                0
                                                Это вы сейчас про какой инструмент говорите?
                                                  –5
                                                  Любые типизирующие надстройки над JS вроде Flow или TypeScript. Тут надо понимать, что JS по своей природе создан для быстрой разработки. Они эту скорость убивают своими закидонами, давая сомнительные преимущества вроде нахождения тривиальных ошибок ценой огромных затрат на поддержку. Сколько я не спрашивал, никто так и не смог привести хоть какие-то научно обоснованные цифры, что эти надстройки хоть чем-то оправданы для профессиональной разработки.
                                                    +1

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

                                                      –2
                                                      Если обмазаться линтерами и писать с типами, то будет прилизанный говнокод с типами. Таких примеров тоже полно. Качество даёт подход и процессы, а не технические средства, проверяющие 5% ошибок, которые всё равно будут быстро устранены в ходе разработки.

                                                      Большинство ошибок связаны с логикой и непредвиденными обстоятельствами, с ними помогут только хорошо зарекомендовавшие себя практики: код-ревью и тестирование.

                                                      Характерно, что фанатики только минусуют, но не могут привести вообще никаких объективных аргументов.
                                                        +4

                                                        Фанатиком выглядите именно вы.
                                                        И чем чаще употреблять выражения типа "говнокод", "обмазываться" — тем больше видна степень именно вашей упоротости.
                                                        Как код-ревью поможет если ваш слабо-типизированый код используют другие — непонятно.
                                                        Быстрая устранимость ошибок, конечно же, у вас с пруфами, да?

                                                          –1
                                                          Странные претензии, типично бездумно-фанатичные.
                                                          И чем чаще употреблять выражения типа «говнокод», «обмазываться» — тем больше видна степень именно вашей упоротости.
                                                          Так это не я, это те, кто говорят, что якобы без типизации нельзя написать хороший код. Можно. Это вообще несвязанные вещи. Более того, я за всю карьеру не встречал сколь-либо значимых проблем из-за слабой типизации. Зато были ошибки только из-за навязывания без достаточных проверок.
                                                          Как код-ревью поможет если ваш слабо-типизированый код используют другие — непонятно.
                                                          Вы когда-то участвовали в код-ревью? Понимаете его цели? Вкратце — это процесс общения между разработчиками, обсуждение полезно, улучшает код. К тому же лишняя пара незамыленных глаз никогда не помешает.
                                                          Быстрая устранимость ошибок, конечно же, у вас с пруфами, да?
                                                          Вы пишете код, совсем не проверяя работу что-ли? Как только вы начинаете проверять, находите ошибки. Опять же типизация ничего тут не меняет и не добавляет никакой ценности. Только тормоза на компиляцию и удовлетворение описания типов.

                                                          Складывается впечатление, что сторонники типизации добавляют её что бы не проверять то, что они пишут. Результат немного предсказуем. Вы из этой когорты? Почему такие странные вопросы?
                                                            +2

                                                            Я начинаю писать и у меня не компилируется, если типы не совпадают.
                                                            Без сборки, запуска тестов, деплоя на интеграцию и прочее.
                                                            Я вас удивлю, но даже просто IDE в процессе написания проверяет корректность, т.е. "Build" делать не нужно — красненьким подчеркнут заранее а подавляющем большинстве случаев.


                                                            Сторонники типизации, как раз, проверяют, что пишут, неявно.
                                                            А упорные фанаты слабой типизации — вчитываются в код и верят в тесты.


                                                            Какой же результат вам предсказался, не стесняйтесь, расскажите.

                                                              0
                                                              Я начинаю писать и у меня не компилируется, если типы не совпадают.
                                                              Без сборки, запуска тестов, деплоя на интеграцию и прочее.
                                                              Только не слишком большой проект без тайпскрипта компилируется порядка минуты или меньше, а с тайпскриптом порядка 5 минут или больше. Пока код не скомпилируется разработчик ничего не делает.

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

                                                                При чём тут ваши локальные проблемы с ТС?
                                                                Еще раз повторюсь, я не компилирую, мне IDE на лету анализирует, перед билдом.


                                                                Вы притворяетесь, что не понимаете, что сильная типизация не отменяет тесты и контроль качества.
                                                                А вот слабая перекладывает ответственность на последующие шаги, такие как — тесты, интергация, QA и т.п.
                                                                Зато быстро собирается (хотя сборка — это мизер во времени разработки).

                                                              +2
                                                              Складывается впечатление, что сторонники типизации добавляют её что бы не проверять то, что они пишут. Результат немного предсказуем. Вы из этой когорты? Почему такие странные вопросы?

                                                              Я тут картинку вспомнил,


                                                              извините

                                                              image

                                                                0
                                                                По горизонтальной оси скорость разработки, надо полагать.
                                                                  +1

                                                                  Интересная интерпретация диаграмм Венна.


                                                                  На самом деле количество крешей в проде.

                                                                    –1
                                                                    Ну, вот и до вранья дошли. Или есть настоящие пруфы про креши?
                                                                      +2

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

                                                                    +1

                                                                    Отличное предположение, знаковое я бы сказал.
                                                                    У вас какой-то пунктик на скорости разработки? Сколько символов в минуту нужно генерировать, чтобы не оказаться "обмазаным"?

                                                          +1
                                                          JS по своей природе создан для примитивной разработки

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

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

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

                                                              Спасибо, буду знать что выбрать, если нелегкая занесет во фронтенд :) и если не удастся пролоббировать использование Blazor %)

                                                                0

                                                                Мы все держим за вас кулачки. Вебасм в президенты!

                                                        0
                                                        Рекомендую взглянуть либо в сторону функциональных языков(haskell, ocaml) либо в сторону crystal. Они статически типизировны, но в них типизация хорошо проработана и указывать типы там требуется редко.
                                                          0

                                                          Это пока вы не начинаете использовать современный хаскель с неразрешимым выводом типов.

                                                          0

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

                                                        –1

                                                        От типизации зависит, поймает она 5%, 95% или 100% ошибок.


                                                        Опыт показывает, что на некоторых языках достаточно интеграционных тестов, и все.

                                                          –1
                                                          Только сегодня писали про сложность O(n²). Как типизация поймает такую ошибку? Как подскажет, что выбран неверный алгоритм? Как подскажет, что код состоит из перемешанный лапши? Да никак. Большинство ошибок связаны с логикой и непредвиденными обстоятельствами, 100% ошибок ловимых типизацией — это 5% от настоящих ошибок.

                                                          Зато пропагандисты пишут, что она поможет найти ошибки типа раньше. Явный запашок, что тесты не пишутся вообще.
                                                            0

                                                            А как тесты поймают ту ошибку, если они будут ограничены таким размером, что n будет достаточно мало?


                                                            А ошибку в микрософтовском STL, про которую писали чуть ранее, типы бы вполне могли поймать.


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

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

                                                              +1

                                                              Вы постоянно пишете какие-то цифры, но не приводите их источники.
                                                              Откуда 5%? Почему не 10% или 30%?
                                                              Что есть настоящие ошибки?
                                                              Вы уверены в своих тестах? На каком основании? Покрытие — ещё не показатель, если что.

                                                                0
                                                                Кстати да. Тесты тоже могут содержать ошибки, как бы странно это ни звучало для некоторых.
                                                                +1
                                                                А кто сказал, что в случае по ссылке — ошибка кодирования? Не было требования, ограничивающего скорость выполнения на большом массиве данных, и/или требования, ограничивающего приоритет выполнения задачи — получите, распишитесь. Задача формально решена, проходит тесты и ревью, а то, что у кого-то из пользователей из-за этого могут возникнуть неудобства — так это в следствие незапланированного режима эксплуатации. В одном из следующих апдейтов предусмотрим работу в вашем режиме, если вы настолько важный клиент.

                                                                Верность алгоритма покажет только эксплуатация. (Ну или тестирование модели, но на этом этапе именно кода еще не написано ни строчки)
                                                                Корректность реализации алгоритма покажут тесты.
                                                                Корректность записи алгоритма покажет компилятор/анализатор.

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

                                                                  Что такое «корректность реализации алгоритма сортировки» и какие тесты её покажут?

                                                                    0

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

                                                                      0

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

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

                                                                        Ну и наконец встречный вопрос: а к чему Вы клоните? Что тесты могут быть несовершенны и иметь, например, дырявое покрытие? Так я с этим не спорю. Если я где-то был по Вашему мнению не прав — прошу указать.
                                                                          +1
                                                                          Если в спецификации будет черным по белому написано, что алгоритм состоит из цепочки «если-то», и такая спецификация каким-то чудом пройдет до этапа кодирования, то да: такая реализация будет корректным отображением документа в код и это будет подтверждено тестами.

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


                                                                          Что тесты могут быть несовершенны и иметь, например, дырявое покрытие?

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


                                                                          Доказать могут только, э, формальные доказательства. Как пример.

                                                                            0
                                                                            Ну да. Мне определенно стоило начинать свои тезисы с приставки «не-»: неверность, некорректность и т.п. Как всегда спешу и не задействую мозг на полную.

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

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

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

                                                                  Такую — никак. Зато чудесно поймает ошибки при рефакторинге. Или вы пишете идеальную архитектуру с первого раза?

                                                                    0

                                                                    Интересный подход!
                                                                    Типизация не ловит косячные алгоритмы (если только это не какой-нибудь идрис), поэтому не будем обмазываться типами, не будем обмазываться ассертами, лучше будем обмазываться всеядной динамикой и скрещивать пальцы, что оно как-то работает.
                                                                    Всё равно умрём, давайте умрём от наркотиков, хотя бы первое время будет весело.


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


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

                                                                      0
                                                                      Я пишу:
                                                                      помогут только хорошо зарекомендовавшие себя практики: код-ревью и тестирование.
                                                                      В ответ:
                                                                      Юнит-тесты тоже не нужны…
                                                                      Фанатики такие фанатики! Лишь бы что сболтнуть!
                                                                        0

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

                                                                          0

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


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


                                                                          Да и код-ревью легко пролюбить в глаза с одним лишним пробелом. Статический анализатор мог бы найти этот опасный код по формальным признакам, но вы от него отказались.

                                                                    0

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

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

                                                                      Изначально на месте JS предполагался диалект Scheme. Наверняка дела с ним обстояли бы намного лучше. Только вот народу C-подобный синтаксис подавай, не хочет напрягать мозги, переучиваясь на другой. Вот так мы и получили то, что получили.
                                                                    +3

                                                                    Это проблемы слабой, а не динамической типизации.


                                                                    Пример на C:


                                                                    printf(3 + "abcdefg"); # stdout: defg

                                                                    (да, я знаю, что стринга, это указатель в C)

                                                                      +4
                                                                      Да, если нужно выстрелить в ногу — тут без C никак ;)
                                                                      Но на эту конструкцию хоть warning у некоторых компиляторов есть.
                                                                        0

                                                                        Честно говоря не уверен, что мой пример с С правильный (не успел удалить).
                                                                        В "строгом" Delphi есть тип PChar (Pointer to char) к которому можно добавить число, но это не мешает ему быть "строгим". Так что, это больше похоже на переопределение методов как в "строгом" Python с "abc" * 3.


                                                                        Кто-то может привести лучший вариант, почему в С официально слабая типизация? (примеры неявного конвертирования)

                                                                          0
                                                                          Правильный пример ;)
                                                                          clang выдает warning.
                                                                          gcc даже предупреждений не выдает. Логично, int + char* == char* + int == char* со смещением. В каждом любом проекте на C эта конструкция присутствует.

                                                                          почему в С официально слабая типизация?

                                                                          Так на этом построена реализация массивов через указатели в С. И вся сишная магия указателей.
                                                                    +7
                                                                    Боже пусть подобные хаки будут только в твитере, а не в продуктивном коде
                                                                      0
                                                                      +1
                                                                      Сейчас же модно «собирать» CSS и JavaScript.
                                                                      Вот в инструментах сборки и сделать такие codestyle-правила, чтоб не попадало на прод.
                                                                      0
                                                                      Так и не понял, зачем текст вставлять картинками.
                                                                        0

                                                                        Чтобы не скопировали

                                                                        0
                                                                        Арифметические операции с float/double имеют такую особенность в любом языке. Я ради любопытства на собеседовании спрашиваю, как люди работают с суммами (если человек явно указывают разработку чего либо имеющего отношение к финансам), дак вот почти никто ничего сказать не может, и это пугает, черт побери.
                                                                          0
                                                                          В других языках, в дополнение к float/double, бывают другие типы. Их названия выглядят наподобие BigDecimal, ScaledDecimal, DECIMAL, DECFLOAT etc. IBM даже аппаратно поддерживает DECFLOAT в своих процессорах. Вот почему в процессорах Intel и в MS Excel этого нет — реальная загадка.
                                                                            0
                                                                            А что тут говорить? Разработчику дают правила округления, вот как скажут так и будет писать.
                                                                              0
                                                                              Для начала неплохо бы знать, что будут проблемы. А потом можно и bcmath или *100 или округление. К тому же не правилами округления едиными.
                                                                                0
                                                                                Зависит от сферы, в ФИНТЕХ указывают и количество знаков после запятой которое должно быть при вычислении и в какую сторону округлять. И если делать согласно ТЗ то проблем не будет. А если это самопальная ERP за 200$ а вместо ТЗ друг рассказал… то да…
                                                                            +1
                                                                            Судя по задачам, я, выходит, матерый жабаскриптер в квадрате :) А всего то 13 лет пишу на нем :D На самом деле представленные кейсы весьма простые, в интернетах можно найти и более не очевидные задачки.
                                                                              0
                                                                              Я регулярно собеседую людей, у которых опыт JS года два и которые хотят его преподавать.
                                                                                0
                                                                                Вот завидую самоуверенности некоторых :D Хотя может там 2 года именно коммерческой разработки, а не кодинга, а суммарно, скажем, 4 года, или просто гении по жизни :) Вообще вполне логично- сейчас 3-4 месяца курсов и джун, год и мидл, 2- уже сеньйор, пора делится мудростью)
                                                                                Не понимаю как можно хотеть преподавать и зачем это им, как по мне, для разраба это скучнейшее и совсем невыгодное занятие, к тому же большинство из нас около-интроверты и преподавание выматывает.
                                                                                  0
                                                                                  Вспоминая университетскую практику, где к преподаванию младшим курсам старшекурсники вполне привлекались, а уже для аспирантов педнагрузка была просто обязательной…
                                                                                    +1

                                                                                    У нас асперы никогда не читали лекции, а максимум вели семинары и обсуждали задачки.


                                                                                    А у меня педпрактика вообще ограничилась проверкой ЗФТШ.


                                                                                    Скрытый текст

                                                                                    Которую я за ящик пива зааутсорсил второкурам.

                                                                                      0
                                                                                      Педнагрузка включает в себя не только лекции, семинары вполне покатят.
                                                                                      Ну и да, в разных ВУЗах теоретически могло быть по-разному.
                                                                                –2
                                                                                Банан не работает. Так работает:
                                                                                let a='a';('b'+'a'+ ++a+'a').toLowerCase();
                                                                                Убрать переменную — не выйдет, убрать пробел — тоже. А так получается очевидно.
                                                                                  +2
                                                                                  >>('b'+'a'+ +'a'+'a').toLowerCase()
                                                                                  >>"banana"
                                                                                  

                                                                                  За кавычками попробуйте проследить, за количеством "+" и не забудьте про пробел между двумя "+"
                                                                                    0
                                                                                    Оу. Не заметил пробела между ними. Ваша правда.
                                                                                    На скриншоте просто выглядит как два плюса подряд.
                                                                                  0
                                                                                  Вот этого: ('b'+'a'+ +'a'+'a').toLowerCase() достаточно — на выходе будет «banana»
                                                                                  Между двумя плюсами пробел действительно обязателен.
                                                                                    0
                                                                                    Баян, на хабре уже как минимум 2 статьи про эти же задачки.
                                                                                      0
                                                                                      В первом примере не сразу дошло, что между плюсами есть пробел. Забавно. Остальные примеры — довольно банальны для всех, кто знаком с JS.

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

                                                                                      Самое читаемое