Очень не хватает деталей и подробностей. Где именно, например, на вашей схеме Ignition, SparkPlug и TurboFan? Будет круто, если следующие статьи будут рассматривать устройство V8 более подробно.
Целью было просто познакомиться с API на непривычном мне языке, и написать хоть что-нибудь. В итоге написал очень простое приложение-таймер; его код тоже есть в репозитории.
Насколько я понимаю, простые модули для хеш-таблиц выбирают для того, чтобы было меньше коллизий при использовании определенных алгоритмов хеширования. Условие, насколько помню, даже более слабое - модуль в хеш-таблице должен быть взаимно простым с неким параметром, который используется в алгоритме хеширования. Это легче всего организовать, если либо оба будут простыми числами, либо если одно будет просым больше двух, а второе - степенью двух.
Мои знания алгоритмов уже не те, что раньше, но вот тут есть более подробный разбор (на английском, правда). Там, насколько вижу, и алгоритм из GCC вскользь упоминается.
А конкретная последовательность простых такая для того, чтобы они росли примерно как степени двойки. Это позволяет иметь, в среднем, константное время на переаллокацию и перехеширование таблицы.
То, что вы говорите, не похоже на правду. M % N для положительного целого N и неотрицательного целого M всегда лежит в [0; N-1]. Даже распределение, насколько понимаю, должно быть равномерным для равномерно распределенного M.
Без понятия, у меня никогда не возникало такой задачи
Вы ни разу не рефакторили код? Или всегда делали это исключительно вручную, даже на больших кодовых базах?
я не представляю как эта задача и статья с этим связаны.
В языках с типами алгоритм автоматического рефакторинга "переименовать поле T.a в T.b" выглядит, грубо говоря, так: найти все выражения "доступ к полю объекта", где объект имеет тип T, а поле - имя a, и заменить в них a на b.
Как это сделать в языках без типов (без вывода этих самых типов самим алгоритмом рефакторинга) - я не представляю.
Пример привел потому, что в этом примере такой рефакторинг невозможно сделать простой автозаменой.
Вот вам два примера работающего кода (...) И где тут какая-то логическая ошибка?
Я не могу сказать, потому что я не понимаю, какая логика задумывалась в этом коде. Логическая ошибка - это ошибка именно в построении алгоритма.
А мы про это сейчас не говорим. Мы не говорим о качестве кода.
Статическая типизация буквально используется как средство поддержания качества кода. Как средство переложить часть задач по проверке логических ошибок на компилятор.
А ожидание через именование это вообще капец конечно.
Так в том и дело, что в языках без типов для формирования ожиданий есть только имя переменной и комментарии.
Еще скажите что count не имеет права быть ничем кроме int. я обязан рассматривать все случае, например вот такие я как хочу так и именую переменные и нет никого на планете кто мог бы мне навязать свою точку зрения в этом вопросе.
Я очень рад, что мне не придется читать ваш код.
Вашу точку зрения понял. Свою, думаю, уже объяснил достаточно хорошо. Не уверен, что смогу вас переубедить, поэтому спор дальше продолжать нет смысла :)
Аналогичный пример, где важны именно типы полей, тоже несложно придумать. Например, я хочу поменять Person.name: string на Person.name: { first: string, last: string }. Или Book.title: string на Book.title: { translated: string, original: string }.
Мне пример ничем не поможет, так как он не относится к предмету разговора.
Так ответьте на вопрос: как сделать такое переименование поля автоматически без статических типов?
В типизированном ЯП это ошибка, в нетипизированном - это само собой разумеющееся явление - оно так работает из коробки.
Это логическая ошибка в вашем коде. Если вы заявляете (типами, если нет типов - комментами или названием переменной), что у нее один набор возможных значений, а потом присваиваете ей другие - это логическая ошибка.
В нетипизированных ЯП нет вообще понятия "целое", есть просто переменная в которую можно писать всё что угодно.
Это понятие все равно существует на уровне логики вашей программы. Если в переменную записывается буквально "что угодно", то это повод поругаться на это на код-ревью, потому что обычно в конкретной переменной ожидают достаточно определенный набор значений. Вряд ли кто-то будет ожидать 1.5 в переменной count, например. Или юзера в переменной order.
При этом в типизированных ЯП строка a += 0.1; сразу перевозбудит компилятор, а в нетипизированном это будет работать без проблем.
Я не уверен, что здесь значит "без проблем". Если вы заявляете, что в переменной может быть только целое число, а потом записываете туда дробное - это явная логическая ошибка. Если же это переменная допускает дробные числа - тогда просто не нужно объявлять ее как целую.
Или вы жалуетесь на то, что автовывод типов не понял по var a = 1, что эта переменная может быть дробной? Так и я не понял, на самом деле.
Как без типов и без ручного просмотра всего кода переименовать Book.name так, чтобы не задеть Person.name? Вот в таком коде, например (специально привел без типов):
function formatRating(bestsellers) {
return `The top bestseller is: ${bestsellers[0].name}`;
}
function formatFamily(brother, sister) {
return `${brother.name} + ${sister.name} are brother and sister`;
}
Типизация, по моему опыту, как раз очень сильно помогает при прототипировании, когда в голове нет до конца оформленной картины того, как именно код будет выглядеть.
Есть у меня, к примеру, код:
class Person {
name: string;
// ...
}
class Book {
name: string;
// ...
}
И уже есть код, использующий оба этих класса. А потом - прототипирование же - я понимаю, что Book.name- это не лучший выбор имени, и что лучше это будет Book.title. Авторефакторинг, если он знает о типах, может мне одним нажатием F2 переименовать везде Book.name, но оставить нетронутым Person.name.
Аналогично - если я вдруг понял, что мне нужно иметь отдельно Person.firstName и Person.lastName - я удаляю старое поле, добавляю новое, а затем прохожусь по всем местам, где компилятор говорит мне про ошибку "поле Person.name не найдено", и правлю их.
А в старом коде типы аналогичным образом очень помогают при рефакторингах. Я не знаю, как в языке без типов можно не бояться переименовывать поля. Можно обвешаться юнит-тестами на доступ к каждому публичному полю и методу, да - но как раз скорость разработки от этого, кажется, очень просядет.
TypeVar в целом выглядел как костыль. Рад, что от него избавляются.
Еще, например, forward references поддерживаются без костылей:
# старый костыль
# `: TypeAlias` можно не писать, но тогда
# это выглядит еще больше как просто строка
FooStr1: TypeAlias = "Foo1[str]"
T = TypeVar('T')
class Foo1(Generic[T]):
...
# новый синтаксис
type FooStr2 = Foo2[str]
class Foo2[T]:
...
def validate_codes(codes):
for code in codes:
if not isinstance(code, int):
warn(f"wrong code: {code}")
return False
return True
def extract_valid_codes(codes_list):
return [c for c in codes_list if validate_codes(c)]
Ну это вопрос привычки. Привыкаешь к такому синтаксису и потом легко читать.
Я не спорю, что я не привык. Но мне кажется, что это одна из наиболее непривычных фич Python, и я не уверен, что эта непривычность оправдана.
Так без всего этого разрабатывать как раз бывает неудобно.
Вот тут выше только что, опять-таки, обсуждали, что в Python нужно либо брать из библиотеки, либо реализовывать самому функцию find(iterable, predicate). Ее, например, без дженериков адекватно не типизируешь. И так с очень многими библиотечными, "не-бизнесовыми" функциями.
Я не спорю, что условие задачи можно поменять так, чтобы именно тот синтаксис был уместным. Более читаемым он от этого не станет.
Можно написать так
for iterator in iterators:
total = 0
for item in iterator:
if isinstance(item, int):
total += item
else:
break
else:
result.append(total)
Можно так
for iterator in iterators:
total = 0
for item in iterator:
if not isinstance(item, int):
break
total += item
else:
result.append(total)
Хотя мне больше нравится такой вариант:
for iterator in iterators:
valid = True
total = 0
for item in iterator:
if not isinstance(item, int):
valid = False
break
total += item
if valid:
result.append(total)
В нем одновременно есть и "отрицательное условие" if not ...: break (потому что именно так выглядит привычный паттерн проверки предусловий), и нет визуальной путаницы, к чему именно относится else (повторюсь, по другим языкам мне намного привычнее ассоциировать его с ближайшим if, а не с циклом).
Наверное, из нашей ветки это не очень понятно, но я (и @AMDmi3, думаю, тоже) предполагал, что найденный элемент будет еще где-то потом использоваться. В ваших вариантах только проверяется наличие элемента: any()возвращаетbool.
Грубо говоря, представьте, что после всех наших отрывков кода стоит return x.
Да, есть серверы, которые могут работать с заранее сжатыми файлами. Например, nginx.
Есть CodeQL, например.
Очень не хватает деталей и подробностей. Где именно, например, на вашей схеме Ignition, SparkPlug и TurboFan? Будет круто, если следующие статьи будут рассматривать устройство V8 более подробно.
Целью было просто познакомиться с API на непривычном мне языке, и написать хоть что-нибудь. В итоге написал очень простое приложение-таймер; его код тоже есть в репозитории.
Насколько я понимаю, простые модули для хеш-таблиц выбирают для того, чтобы было меньше коллизий при использовании определенных алгоритмов хеширования. Условие, насколько помню, даже более слабое - модуль в хеш-таблице должен быть взаимно простым с неким параметром, который используется в алгоритме хеширования. Это легче всего организовать, если либо оба будут простыми числами, либо если одно будет просым больше двух, а второе - степенью двух.
Мои знания алгоритмов уже не те, что раньше, но вот тут есть более подробный разбор (на английском, правда). Там, насколько вижу, и алгоритм из GCC вскользь упоминается.
А конкретная последовательность простых такая для того, чтобы они росли примерно как степени двойки. Это позволяет иметь, в среднем, константное время на переаллокацию и перехеширование таблицы.
То, что вы говорите, не похоже на правду.
M % Nдля положительного целогоNи неотрицательного целогоMвсегда лежит в[0; N-1]. Даже распределение, насколько понимаю, должно быть равномерным для равномерно распределенногоM.Вы ни разу не рефакторили код? Или всегда делали это исключительно вручную, даже на больших кодовых базах?
В языках с типами алгоритм автоматического рефакторинга "переименовать поле
T.aвT.b" выглядит, грубо говоря, так: найти все выражения "доступ к полю объекта", где объект имеет типT, а поле - имяa, и заменить в нихaнаb.Как это сделать в языках без типов (без вывода этих самых типов самим алгоритмом рефакторинга) - я не представляю.
Пример привел потому, что в этом примере такой рефакторинг невозможно сделать простой автозаменой.
Я не могу сказать, потому что я не понимаю, какая логика задумывалась в этом коде. Логическая ошибка - это ошибка именно в построении алгоритма.
Статическая типизация буквально используется как средство поддержания качества кода. Как средство переложить часть задач по проверке логических ошибок на компилятор.
Так в том и дело, что в языках без типов для формирования ожиданий есть только имя переменной и комментарии.
Я очень рад, что мне не придется читать ваш код.
Вашу точку зрения понял. Свою, думаю, уже объяснил достаточно хорошо. Не уверен, что смогу вас переубедить, поэтому спор дальше продолжать нет смысла :)
Аналогичный пример, где важны именно типы полей, тоже несложно придумать. Например, я хочу поменять
Person.name: stringнаPerson.name: { first: string, last: string }. ИлиBook.title: stringнаBook.title: { translated: string, original: string }.Так ответьте на вопрос: как сделать такое переименование поля автоматически без статических типов?
Это логическая ошибка в вашем коде. Если вы заявляете (типами, если нет типов - комментами или названием переменной), что у нее один набор возможных значений, а потом присваиваете ей другие - это логическая ошибка.
Это понятие все равно существует на уровне логики вашей программы. Если в переменную записывается буквально "что угодно", то это повод поругаться на это на код-ревью, потому что обычно в конкретной переменной ожидают достаточно определенный набор значений. Вряд ли кто-то будет ожидать
1.5в переменнойcount, например. Или юзера в переменнойorder.Привел пример в комменте выше.
Я не уверен, что здесь значит "без проблем". Если вы заявляете, что в переменной может быть только целое число, а потом записываете туда дробное - это явная логическая ошибка. Если же это переменная допускает дробные числа - тогда просто не нужно объявлять ее как целую.
Или вы жалуетесь на то, что автовывод типов не понял по
var a = 1, что эта переменная может быть дробной? Так и я не понял, на самом деле.Как без типов и без ручного просмотра всего кода переименовать
Book.nameтак, чтобы не задетьPerson.name? Вот в таком коде, например (специально привел без типов):Типизация, по моему опыту, как раз очень сильно помогает при прототипировании, когда в голове нет до конца оформленной картины того, как именно код будет выглядеть.
Есть у меня, к примеру, код:
И уже есть код, использующий оба этих класса. А потом - прототипирование же - я понимаю, что
Book.name- это не лучший выбор имени, и что лучше это будетBook.title. Авторефакторинг, если он знает о типах, может мне одним нажатием F2 переименовать вездеBook.name, но оставить нетронутымPerson.name.Аналогично - если я вдруг понял, что мне нужно иметь отдельно
Person.firstNameиPerson.lastName- я удаляю старое поле, добавляю новое, а затем прохожусь по всем местам, где компилятор говорит мне про ошибку "полеPerson.nameне найдено", и правлю их.А в старом коде типы аналогичным образом очень помогают при рефакторингах. Я не знаю, как в языке без типов можно не бояться переименовывать поля. Можно обвешаться юнит-тестами на доступ к каждому публичному полю и методу, да - но как раз скорость разработки от этого, кажется, очень просядет.
TypeVarв целом выглядел как костыль. Рад, что от него избавляются.Еще, например, forward references поддерживаются без костылей:
Я не спорю, что я не привык. Но мне кажется, что это одна из наиболее непривычных фич Python, и я не уверен, что эта непривычность оправдана.
Так без всего этого разрабатывать как раз бывает неудобно.
Вот тут выше только что, опять-таки, обсуждали, что в Python нужно либо брать из библиотеки, либо реализовывать самому функцию
find(iterable, predicate). Ее, например, без дженериков адекватно не типизируешь. И так с очень многими библиотечными, "не-бизнесовыми" функциями.Я не спорю, что условие задачи можно поменять так, чтобы именно тот синтаксис был уместным. Более читаемым он от этого не станет.
Можно написать так
Можно так
Хотя мне больше нравится такой вариант:
В нем одновременно есть и "отрицательное условие"
if not ...: break(потому что именно так выглядит привычный паттерн проверки предусловий), и нет визуальной путаницы, к чему именно относитсяelse(повторюсь, по другим языкам мне намного привычнее ассоциировать его с ближайшим if, а не с циклом).Наверное, из нашей ветки это не очень понятно, но я (и @AMDmi3, думаю, тоже) предполагал, что найденный элемент будет еще где-то потом использоваться. В ваших вариантах только проверяется наличие элемента:
any()возвращаетbool.Грубо говоря, представьте, что после всех наших отрывков кода стоит
return x.Что характерно, подсветка синтаксиса на вашем скриншоте работает прекрасно.
Вам выдают
SyntaxError, да, но это и понятно, если у вас версия питона еще не 3.12.В моем примере использованы именно эти "третьи" кавычки внутри подстановки внутри таких же кавычек. И все спокойно работает.
Все, что сложнее наивных регулярок, должно подсвечивать нормально. Хабр вот справляется: