Обновить
37

Пользователь

0,4
Рейтинг
8
Подписчики
Отправить сообщение

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

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

На Java, C# и Python обёртки вокруг C-шных функций из Win32 API будут выглядеть примерно так же. Красота самих обёрточных функций никого не волнует. Зато Rust-функция принимает на один параметр меньше.

Это не то чтобы однозначно хорошая идея: если видеопамять хранит bitmap-ы, то её нужно сильно больше, чем видеопамяти под символы. А если сравнить цены на RAM между 1973 и 1982 годами...

Насчёт того, кто придумал: у ZX80 и ZX81 видеосигнал формировал сам Z80, читал коды символов из экранного буфера и превращал их в пиксели для горизонтальной развёртки. Очень дешёвый вариант: компактное хранение и мало микросхем. В ZX Spectrum уже ULA отслеживала номер линии, формировала видео-адрес, читала байт и отдавала пиксели, там не сильно сложно, сдвиговый регистр.

Вроде бы с первой версии, а это уже 30 лет. 30 лет, в dotnet люди могут плюс-минус эффективно управлять памятью. И вопрос был именно про это.. как так получилось, что в java настолько консервативная, что это не завезли за долгие годы

Не было запроса.

  1. Далеко не факт, что многократное копирование эффективнее, чем копирование ссылок

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

  3. А помимо бокс-классов для скаляров не так уж и много value-типов в стандартной библиотеке. String, например, не будет value-типом, он и в C# таковым не является, при этом он часто хранится в коллекциях.

Пока не будет изменения в generic-ах, значимость value-типов сильно преувеличена.

Я немного почитал про C#, и сравнительная история выглядит так: в старых версиях и Java, и C# списки были сделаны как обёртки над массивом Object-ов, потому что он является общим предком объектов. При этом value types, чтобы их поместить в такой массив, боксятся. Как я понимаю, на каждый value type генерируется бокс-класс. Так что на уровне хранения в коллекциях память использовалась примерно одинаково. Потом в C# добавили generic-и, и для них сделали новые классы коллекций, там на каждый тип элемента для списков можно использовать массив подходящего типа и избежать боксинга. А в Java при добавлении generic-ов решили не ломать совместимость и не плодить коллекций, а раз уж всё равно боксить, то нафиг value types?

мы можем передавать value types как по значениюvoid Foo(MyStruct arg), а можем по ссылке void Foo(ref MyStruct arg)

выглядит как чисто performance hint.

Это не совсем struct. Насколько я знаю, struct не аллоцируются на куче, а в кадре стека или инлайнятся в массивы/классы и всегда копируются. Для value-классов то, где оно будет аллоцироваться, будет решать jit-компилятор, вполне возможно, что он будет аллоцирован на куче, ссылка передаваться из кадра в кадр, а при сохранении в массив произойдёт копирование полей.

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

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

А что убедительно показал опыт Японии, Южной Кореи и Тайваня?

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

В C определение функции с именем f означает, что компилятор в пространстве кода сформирует код и символ f, который указывает на этот код и значение которого поменять нельзя. А в Scheme объявление (define f (lambda (x) (+ x 2))) означает, что глобальная переменная f имеет значение-функцию. При вызове функции через имя f, например, так (f 3), будет взято текущее значение этой переменной, а именно значение-функция, определённая лямбдой, и выполнена для переданного аргумента. Значение глобальной переменной можно поменять, и f может начать ссылаться на другую функцию. Когда к f обратятся в следующий раз, то там будет новое значение, но если код из f выполнялся на момент изменения, то он продолжит выполнение до выхода из функции.

REPL - это такое особое Lisp-приложение, в котором поток исполнения в бесконечном цикле читает ввод пользователя, парсит его, выполняет eval для динамического выполнения кода и печатает результат. Поэтому хоть в REPL, хоть не в нём можно поменять значение глобальной переменной с одной функции на другую, пропатчив код. Не у всякого рабочего Lisp-приложения это возможно, обычно для этого приложение, помимо основных рабочих потоков, должно специально оставить REPL-поток, через который можно подключиться и это сделать.

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

Для LLM-ной статьи в целом сойдёт. Остальное придирки.

Маккарти взял из лямбда-исчисления Алонзо Черча нотацию LAMBDA для обозначения функций. Это стало основой синтаксиса Лиспа. Но сам Маккарти позже признавался, что в момент создания языка не очень-то разбирался в лямбда-исчислении - взял нотацию, потому что она казалась удобной и пошел дальше. Позже Дэвид Парк заметил, что рекурсивную LABEL-нотацию можно выразить через конструкцию на LAMBDA, аналогичную Y-оператору Черча. Но сама LAMBDA в Лиспе Y-комбинатором не является.

Lambda сама по себе нигде не является Y-комбинатором, ни в Лиспе, ни в математике. Y-комбинатор составляется из Lambda-выражений, и его смысл в том, чтобы функция вызывала саму себя, не имея имени.

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

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

Условные выражения. if-then-else в том виде, в каком это существует в любом языке - тоже Лисп. Звучит странно, но до 1958-го условные переходы реализовывались как низкоуровневые инструкции, а не как выражения, возвращающие значение.

if и сейчас не в любом языке является выражением.

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

Если делать чистый-чистый pull, когда значение зависимости только читается, то нужно постоянно опрашивать зависимый объект: а вдруг он изменился? Поэтому, я думаю, у вас не так, скорее всего при изменении объект через push оповещает всех подписчиков о своём изменении, подписчики помечают себя как нуждающиеся в пересчёте, а потом, когда рендер решит их перерисовать, они через pull подтянут значения. А значит, у вас сведения о зависимостях хранятся, вероятно, так:

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

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

Поправьте, где я угадал неправильно. А если правильно, то вы оптимизируете время пересчёта через немалое количество памяти.

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

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

Я думаю, под кешами подразумевались не кеши процессора, а дисковые кеши и memory-mapped files, эта тема связана с темой виртуальной памяти

Я правильно понял пример со связью, что оно понимает, что таблица с планами стоит слева, а значит правую таблицу нужно сначала сгруппировать по полю связи, и потом сделать left join ? Я к тому, что если эта операция выражается через группировки и join-ы, то зачем выделяется?

Ещё про управление ресурсами хотел спросить: у вас в сессии выделяется какой-то объём памяти под загрузку таблиц с диска и хранение результатов запроса, чтобы быстро пользователю показывать, или результаты забываются, как только их отдали пользователю?

Ну и про название вам, наверное, уже говорили.

  1. много кто помнит

  2. есть документация.

Это мужик, маскирующийся под девушку, то есть точно мудак.

Понравился слог текста.

Сам я слишком стар, чтобы менять работу, а за вас очень рад, удачи вам!

1
23 ...

Информация

В рейтинге
2 754-й
Зарегистрирован
Активность