Вызов должен сделать Int+Int, либо String+String, либо T1+T1, либо T2+T2, либо T3+T3, либо ..., либо T10+T10. Двенадцать разных вариантов (для типов T1-T10 результат вычисляется операциями над полями с разными смещениями, поэтому код заведомо не взаимозаменяем).
def foo(a, b, c):
if a == 0 :
return sum(b, c)
return sum(b, a), c
Совместное распределение типов имеет 155 разных вариантов. Отдельно обращает на себя внимание вариант (Int, Int, Int), потому что он возвращает то ли Int, то ли Tuple[Int, Int], и как прикажете компилировать код возврата из этой функции?
Ну так прикол в том, что я могу написать десяток-другой своих типов с перегруженным +. Что ещё хуже, к моменту когда компилятор добрался до sum , эти типы ещё могут быть не объявлены. Это примерно как требовать сразу инстанцировать шаблонные функции в C++ для всех наборов параметров, которые могут иметь смысл: комбинаторный взрыв плюс невозможность компилировать программу по частям плюс потенциальные бесконечности (a может быть функцией int->int, int->(int->int), int->(int->(int->int)) и так далее, множество допустимых типов бесконечно):
Тесты вообще сами по себе верифицируют примерно нифига. Они осмысленны только в сочетании с какими-то ожиданиями о реализации, иначе для проверки целочисленного сложения пришлось бы писать отдельно тест sum(2,3) == 5, отдельно тест sum(2,4) == 6 и далее ad nauseam.
Если у нас... ну пусть пять типов, то предлагаете компилятору сразу генерировать 625 вариантов функции caller? А если типов 50 (в Python вообще-то свои классы можно создавать)?..
При массовом засилии всех этих типов, разве Вы редко видите надписи "случилось неожиданное"?
Я несколько раз пробовал что-то писать на JavaScript, в разных контекстах и по разным поводам. Каждый, каждый раз по достижении пары тысяч строк у меня начинались ошибки "тут integer интерпретируется как массив" (или наоборот). После того как освоил TypeScript, жить стало проще, даже с добавленным геморроем "напиши заголовки для этих пяти библиотек". Поэтому я довольно сильно уверен что если бы у меня не было этой страховки, я бы такой хрени в кодовых базах на сотни тысяч строк видел бы намного больше, чем вижу сейчас.
а что если пришедшая строка прошла валидацию через jsonschema/иной вариант валидаторов входных данных?
Тогда price должен быть уже int. Иначе откуда я знаю, что она действительно прошла, а не свалилась с другого логического пути, на который программа попадает при вот такой уникальной комбинации условий? (И такие баги я ловил - когда автор кода почему-то посчитал, что строка прошла через операцию экранирования, а она в каком-то случае нет, и оп-паньки, получился невалидный JSON. Замечу что разделение типа "строка" на типы "экранированная строка" и "неэкранированная строка" позволило бы поймать баг до коммита в main.)
или другой вопрос: а что если этот код упадёт, и пользователь получит "случилось нечто неожиданное". Пожмёт плечами и сделает рефреш.
У пользователя упала программа с результатами его работы за последние полчаса. Если после этого пользователь испытает желание взять бензопилу и пойти навестить разработчиков, я его даже пойму.
Безотносительно этого, "мои ошибки не приводят к серьёзным последствиям" по-моему очень плохое основание чтобы относиться к ошибкам наплевательски. Завтра мне нужно делать меньше ошибок, чем я делаю сегодня. Такое снисходительное похлопывание себя по плечу, "да ничего страшного, чувак" этому не способствует.
То есть математику можно так же программировать, как и торговлю. А можно программировать, например, игры в покер.
Я не говорю о предметной области. Я говорю о том, почему я, написав программу, считаю что она будет делать то, что я задумал. "Мне кажется" - не аргумент (людям кажутся верными очень бредовые вещи, я человек, следовательно то что мне кажется верным может быть бредом). Доказательство - это как раз и есть способ быть более уверенным в том, что ненадёжный ты сделал более надёжную конструкцию. Математика просто учит бить себя по рукам при попытке написать менее надёжно, чем мог бы. Компилятор железный, если он способен поймать мои ошибки - я хочу чтобы он это сделал.
Будь то игра в покер или автовзвешиватель рыбы, вопрос один и тот же: почему я считаю, что моя программа корректна? Доказательство - это способ строить глобально корректную конструкцию, удостоверяясь в локальной корректности шагов.
Платить за это спасение ту цену, что предлагают типизированные языки - стрелять по воробьям из пушки.
Возможно я не вижу цены. Всякое конкретное имя в программе - это конкретный символ в доказательстве, он в любом случае имеет конкретный смысл. Обычно этот смысл может быть выражен типом. Если я не вижу чётких границ этого смысла - это не потому что их нет, это потому что у меня хреновое зрение. В функцию может быть передан неожиданный объект? Значит я тупой, "неожиданный" - это не кодовое слово для имманентно загадочного внешнего мира, это недостаточно строгое моё собственное мышление, которое не учло логически возможный вариант.
price.parse::<f64>()
Если price - строка, то в ней может быть "сюрприз" . Я обязан чётко видеть, что именно должна сделать моя функция со строкой такого содержания. (А что если в ней -500? А если произведение приводит к целочисленному переполнению?) Здесь можно задать архитектурный вопрос, а не надо ли вынести санацию значений куда-нибудь в район их получения, но локально обязанность ясно понимать поведение функции при каждом физически возможном входе - это самый минимум для того, чтобы хотя бы начинать надеяться, что итоговая программа не является полным, абсолютным барахлом.
Программирование — это искусство. А искусство не терпит правил.
Контр-тезис: программирование - это овеществлённая математика. Код всякой функции должен одновременно читаться как доказательство того, что эта функция корректна. А доказательства не бывают "на вайбах", "без правил" и тому подобной мутью. "Ну ты же понял что я имел в виду" - это не доказательство, это барахло.
Статическая типизация не спасает от дурака.
Меня она от себя спасала много раз (следует ли это считать подтверждением что я не дурак?..). От банального "присвоить ID типа A переменной, которая во всём остальном коде интерпретируется как ID типа Б" до "заметить что я использую координаты в неверной проекции" или "это не переведённая текстовая строка, пользователю её показывать не надо". И мне чаще приходится сталкиваться с логическими ошибками именно потому, что в функции передаются структуры с типизированными полями, а не массивы "третий компонент обозначает время по Дубаю, четвёртый - число снегоуборочных машин, пятый - бинарное представление PNG с номерным знаком автомобиля".
У Лукьяненко был рассказ, "Временная суета". Субъективно, я не нашёл его хорошим самим по себе (скажем, в сравнении с "Поездом в тёплый край") и не нашёл его ухватывающим дух "Понедельника".
Шаг вперёд: верить тому кто тщательно изучил и не обруган другими кто тщательно изучил.
Шаг назад: верить пересказу мнения тех кто тщательно изучил и не обруган другими кто тщательно изучил.
...
Заблудиться: потерять связь, восходящую к "тщательно изучить", и считать что есть только равноправные мнения людей и собственные глаза. Нет, не равноправные. "Может быть неправдой" подразумевает вероятность, она может быть сильно разной на основе чего-то изученного.
На практике большинство бредовых идей в нашем меметическом пространстве бредовы очевидно. Даже без каких-то сложных социальных графов: опровергатели теории относительности путаются в школьной физике, носители грандиозных идей о возникновении человека не знают анатомии, etc.
На вопрос "чем вы верующие безусловно в круглость земли отличаетесь от тех, кто верит в то, что она плоская" в основном обижаются. Говорят, что они верят в правильное, а плоскоземельцы в неправильное...
И таки-правы, нет? У стратегии "сразу (a priori) иметь правильные убеждения и не заниматься обработкой свидетельств" есть плюсы, особенно по сравнению со стратегией "начать с правильных убеждений, налажать в анализе свидетельств и придти к неправильным".
Даже "классические" курсы со временем меняются (скажем, определение предела через фильтры). А есть ещё приоритеты: что программистам неплохо бы прочитать основы квантовой механики, что численная оптимизация где-то стала важнее дифуров, etc. Скажем, мне в своё время читали отдельную лекцию про углы Эйлера. Сама теория, абстрактно, осталась, но её актуальность для программистов заметно снизилась.
Как бывший олимпиадник, замечаю что лично у меня такого правила не было, ближайшее к нему "если в задаче А затык, реши пока задачу Б, может потом придут умные мысли". Вообще обычно на олимпиаде предполагается что участник напишет что-то осмысленное про все задачи (ну, если он претендует на хороший результат).
Но разработчики TrapC уверяют, что их компилятор минимизирует накладные расходы, выполняя большинство проверок во время компиляции, а не работы программы.
А это как, простите? В этих искусственных примерах - верю, но в них и любой линтер вроде cppcheck ругнётся. А если у меня в функцию передан указатель на начало массива и из совершенно другой переменной выводится его длина, то компилятор же хрен докажет что длина корректная, это нужно анализировать всю программу.
отсутствуют макросы: упрощение синтаксиса и устранение ошибок, связанных с их использованием;
Замена того что делалось макросами на примерно эквивалентные им шаблоны упростит синтаксис? Ну-ну.
Но (код) сохраняет высокую производительность благодаря оптимизации работы компилятора.
Какой-то словесный салат. Работающий эффективно компилятор и компилятор, порождающий эффективный код - это очевидно разные вещи. "Автоматически освобождает память" - программа/среда исполнения, а не компилятор. Если компилятор вставил в код проверку, а не надо ли "автоматически" освободить память - эта проверка съела минимум одну ассемблерную инструкцию, чудес не будет. (А в многопоточной программе такие проверки на произвольную память делать больно, многопоточные структуры стараются разнести во времени работу с данными и выделение памяти.)
При каких-то условиях (не знаю могу хорошо сформулировать каких, не тестировщик) ссылка на комментарии (с /comments/) не позволяет видеть текст поста, в то время как ссылка на пост при прокрутке вниз отображает комментарии.
Желание получить 99/100% времени и что-нибудь хорошее по памяти понятно, но не должно быть частью использования LeetCode как инструмента отбора кандидатов. Если вас мало интересует оптимизация - потому что интересует мало и нефиг отбирать людей по тому что вас интересует мало, если вас сильно интересует оптимизация - потому что нормальная оптимизация делается профилировщиком, а не на глаз. Опять же, результаты LeetCode не отличаются хорошей повторяемостью, особенно по памяти.
Не должна. Проверка условия
[3, "tree", []] == 0
в Python вполне корректна, условие не выполнено.Можете, но как только вы перетащили работу с типами во время исполнения - код от компилятора C++ весело помахал вам ручкой и скрылся в голубой дали.
def surprise(a):
if a==0:
return 42
elif a==1:
return "48"
elif a==2:
return [4, 2]
def sum(a, b):
return a + b
Вызов должен сделать Int+Int, либо String+String, либо T1+T1, либо T2+T2, либо T3+T3, либо ..., либо T10+T10. Двенадцать разных вариантов (для типов T1-T10 результат вычисляется операциями над полями с разными смещениями, поэтому код заведомо не взаимозаменяем).
def foo(a, b, c):
if a == 0 :
return sum(b, c)
return sum(b, a), c
Совместное распределение типов имеет 155 разных вариантов. Отдельно обращает на себя внимание вариант (Int, Int, Int), потому что он возвращает то ли Int, то ли Tuple[Int, Int], и как прикажете компилировать код возврата из этой функции?
Ну так прикол в том, что я могу написать десяток-другой своих типов с перегруженным +. Что ещё хуже, к моменту когда компилятор добрался до
sum
, эти типы ещё могут быть не объявлены. Это примерно как требовать сразу инстанцировать шаблонные функции в C++ для всех наборов параметров, которые могут иметь смысл: комбинаторный взрыв плюс невозможность компилировать программу по частям плюс потенциальные бесконечности (a
может быть функцией int->int, int->(int->int), int->(int->(int->int)) и так далее, множество допустимых типов бесконечно):def foo(a) :
def bar(b) :
return a(b)
return bar
Тесты вообще сами по себе верифицируют примерно нифига. Они осмысленны только в сочетании с какими-то ожиданиями о реализации, иначе для проверки целочисленного сложения пришлось бы писать отдельно тест
sum(2,3) == 5
, отдельно тестsum(2,4) == 6
и далее ad nauseam.Мгм.
def caller(a, b, c, d):
print sum(a, c)
print sum(d, b)
Если у нас... ну пусть пять типов, то предлагаете компилятору сразу генерировать 625 вариантов функции caller? А если типов 50 (в Python вообще-то свои классы можно создавать)?..
Ну так ровно потому, что эффективный двоичный код и нетипизированный язык - штуки несовместимые.
def sum(a, b):
return a+b
И во что прикажете это компилировать? В
add eax, ebx
, вcall string_concat
или во что-то третье?Я несколько раз пробовал что-то писать на JavaScript, в разных контекстах и по разным поводам. Каждый, каждый раз по достижении пары тысяч строк у меня начинались ошибки "тут integer интерпретируется как массив" (или наоборот). После того как освоил TypeScript, жить стало проще, даже с добавленным геморроем "напиши заголовки для этих пяти библиотек". Поэтому я довольно сильно уверен что если бы у меня не было этой страховки, я бы такой хрени в кодовых базах на сотни тысяч строк видел бы намного больше, чем вижу сейчас.
Тогда
price
должен быть ужеint
. Иначе откуда я знаю, что она действительно прошла, а не свалилась с другого логического пути, на который программа попадает при вот такой уникальной комбинации условий? (И такие баги я ловил - когда автор кода почему-то посчитал, что строка прошла через операцию экранирования, а она в каком-то случае нет, и оп-паньки, получился невалидный JSON. Замечу что разделение типа "строка" на типы "экранированная строка" и "неэкранированная строка" позволило бы поймать баг до коммита в main.)У пользователя упала программа с результатами его работы за последние полчаса. Если после этого пользователь испытает желание взять бензопилу и пойти навестить разработчиков, я его даже пойму.
Безотносительно этого, "мои ошибки не приводят к серьёзным последствиям" по-моему очень плохое основание чтобы относиться к ошибкам наплевательски. Завтра мне нужно делать меньше ошибок, чем я делаю сегодня. Такое снисходительное похлопывание себя по плечу, "да ничего страшного, чувак" этому не способствует.
Я не говорю о предметной области. Я говорю о том, почему я, написав программу, считаю что она будет делать то, что я задумал. "Мне кажется" - не аргумент (людям кажутся верными очень бредовые вещи, я человек, следовательно то что мне кажется верным может быть бредом). Доказательство - это как раз и есть способ быть более уверенным в том, что ненадёжный ты сделал более надёжную конструкцию. Математика просто учит бить себя по рукам при попытке написать менее надёжно, чем мог бы. Компилятор железный, если он способен поймать мои ошибки - я хочу чтобы он это сделал.
Будь то игра в покер или автовзвешиватель рыбы, вопрос один и тот же: почему я считаю, что моя программа корректна? Доказательство - это способ строить глобально корректную конструкцию, удостоверяясь в локальной корректности шагов.
Возможно я не вижу цены. Всякое конкретное имя в программе - это конкретный символ в доказательстве, он в любом случае имеет конкретный смысл. Обычно этот смысл может быть выражен типом. Если я не вижу чётких границ этого смысла - это не потому что их нет, это потому что у меня хреновое зрение. В функцию может быть передан неожиданный объект? Значит я тупой, "неожиданный" - это не кодовое слово для имманентно загадочного внешнего мира, это недостаточно строгое моё собственное мышление, которое не учло логически возможный вариант.
price.parse::<f64>()
Если
price
- строка, то в ней может быть"сюрприз"
. Я обязан чётко видеть, что именно должна сделать моя функция со строкой такого содержания. (А что если в ней -500? А если произведение приводит к целочисленному переполнению?)Здесь можно задать архитектурный вопрос, а не надо ли вынести санацию значений куда-нибудь в район их получения, но локально обязанность ясно понимать поведение функции при каждом физически возможном входе - это самый минимум для того, чтобы хотя бы начинать надеяться, что итоговая программа не является полным, абсолютным барахлом.
Контр-тезис: программирование - это овеществлённая математика. Код всякой функции должен одновременно читаться как доказательство того, что эта функция корректна. А доказательства не бывают "на вайбах", "без правил" и тому подобной мутью. "Ну ты же понял что я имел в виду" - это не доказательство, это барахло.
Меня она от себя спасала много раз (следует ли это считать подтверждением что я не дурак?..). От банального "присвоить ID типа A переменной, которая во всём остальном коде интерпретируется как ID типа Б" до "заметить что я использую координаты в неверной проекции" или "это не переведённая текстовая строка, пользователю её показывать не надо".
И мне чаще приходится сталкиваться с логическими ошибками именно потому, что в функции передаются структуры с типизированными полями, а не массивы "третий компонент обозначает время по Дубаю, четвёртый - число снегоуборочных машин, пятый - бинарное представление PNG с номерным знаком автомобиля".
У Лукьяненко был рассказ, "Временная суета". Субъективно, я не нашёл его хорошим самим по себе (скажем, в сравнении с "Поездом в тёплый край") и не нашёл его ухватывающим дух "Понедельника".
Хороший вопрос. Я изначально думал что это из каких-то кавказских легенд.
Поисковик напомнил что есть и русскоязычное ("Мастер и Маргарита"):Но утверждается что на МиМ.
Начало: верить собственным глазам.
Шаг вперёд: верить собственному тщательному изучению.
Шаг назад: верить тому кто тщательно изучил.
Шаг вперёд: верить тому кто тщательно изучил и не обруган другими кто тщательно изучил.
Шаг назад: верить пересказу мнения тех кто тщательно изучил и не обруган другими кто тщательно изучил.
...
Заблудиться: потерять связь, восходящую к "тщательно изучить", и считать что есть только равноправные мнения людей и собственные глаза. Нет, не равноправные. "Может быть неправдой" подразумевает вероятность, она может быть сильно разной на основе чего-то изученного.
На практике большинство бредовых идей в нашем меметическом пространстве бредовы очевидно. Даже без каких-то сложных социальных графов: опровергатели теории относительности путаются в школьной физике, носители грандиозных идей о возникновении человека не знают анатомии, etc.
И таки-правы, нет? У стратегии "сразу (a priori) иметь правильные убеждения и не заниматься обработкой свидетельств" есть плюсы, особенно по сравнению со стратегией "начать с правильных убеждений, налажать в анализе свидетельств и придти к неправильным".
Даже "классические" курсы со временем меняются (скажем, определение предела через фильтры). А есть ещё приоритеты: что программистам неплохо бы прочитать основы квантовой механики, что численная оптимизация где-то стала важнее дифуров, etc.
Скажем, мне в своё время читали отдельную лекцию про углы Эйлера. Сама теория, абстрактно, осталась, но её актуальность для программистов заметно снизилась.
Как бывший олимпиадник, замечаю что лично у меня такого правила не было, ближайшее к нему "если в задаче А затык, реши пока задачу Б, может потом придут умные мысли". Вообще обычно на олимпиаде предполагается что участник напишет что-то осмысленное про все задачи (ну, если он претендует на хороший результат).
То есть просто лжи вам мало, хотите добавить ещё и ложь о том что Х "казнён за лож гражданам"?
А это как, простите? В этих искусственных примерах - верю, но в них и любой линтер вроде cppcheck ругнётся. А если у меня в функцию передан указатель на начало массива и из совершенно другой переменной выводится его длина, то компилятор же хрен докажет что длина корректная, это нужно анализировать всю программу.
Замена того что делалось макросами на примерно эквивалентные им шаблоны упростит синтаксис? Ну-ну.
Какой-то словесный салат. Работающий эффективно компилятор и компилятор, порождающий эффективный код - это очевидно разные вещи. "Автоматически освобождает память" - программа/среда исполнения, а не компилятор. Если компилятор вставил в код проверку, а не надо ли "автоматически" освободить память - эта проверка съела минимум одну ассемблерную инструкцию, чудес не будет. (А в многопоточной программе такие проверки на произвольную память делать больно, многопоточные структуры стараются разнести во времени работу с данными и выделение памяти.)
При каких-то условиях (не
знаюмогу хорошо сформулировать каких, не тестировщик) ссылка на комментарии (с/comments/
) не позволяет видеть текст поста, в то время как ссылка на пост при прокрутке вниз отображает комментарии.Желание получить 99/100% времени и что-нибудь хорошее по памяти понятно, но не должно быть частью использования LeetCode как инструмента отбора кандидатов. Если вас мало интересует оптимизация - потому что интересует мало и нефиг отбирать людей по тому что вас интересует мало, если вас сильно интересует оптимизация - потому что нормальная оптимизация делается профилировщиком, а не на глаз. Опять же, результаты LeetCode не отличаются хорошей повторяемостью, особенно по памяти.