Страницы

Поиск по вопросам

воскресенье, 24 ноября 2019 г.

Что использовать правильней: if(), или try-catch?


Например что использовать, когда нужно создавать колонку в таблице только в том случае, если такой колонки в ней нет?

Я могу, как написать код, который будет проверять, существует ли колонка, и лиш
потом добавлять без ошибок, так могу и без проверки пытаться добавить колонку, обернув метод в try-catch (если есть — перехватится исключение; если нет — колонка добавится).

Результат работы будет одинаковым.

И таких примеров можно привести массу, например можно проверять файлы на существование, и только потом делать копирование, а можно перехватывать исключения.

Какой метод более грамотный, или правильный?
    


Ответы

Ответ 1



Исключения позволяют сделать код чище и понятнее, поскольку с их помощью можно разделит выполнение действий и обработку ошибок. В книге Мартина «Чистый код» этот аспект описан самым первым. При этом, по моему опыту, надо именно разделять код: выносить блок try/catch в отдельны метод. Программист, который будет разбираться с вашей программой, скажет вам «спасибо». Эта конструкция весьма громоздка, даже в простой форме try/finally, и в середине большого метода озадачит кого угодно. Второй плюс исключений в том, что они позволяют передавать дополнительную информацию Функция atoi из C ничего не могла сказать о том, почему именно не удалось конвертировать строку в целое число. int result; result = atoi("123"); /* в result 123 */ . . . result = atoi("foo"); /* что в result? */ В таких языках, как Java и C# вы можете добавить в свой класс исключений необходимы свойства, сочетая какие-нибудь коды ошибок с контекстами вызова и ещё чем-нибудь. Пример: . . . catch (SqlException e) { Console.WriteLine("Ошибка '{2}' в строке {0} процедуры {1}", e.LineNumber, e.Procedure, e.Message); } . . . Разработчики, которые давно и прочно перешли на исключения, делают свой код ещё чище, не возвращая никаких кодов ошибок, в частности, пресловутого null: // Что будет, если в хранилище нет пользователя с указанным userid? // Вернёт null или сгенерирует исключение? User user = userRepository.GetById(userId); В настоящее время считается, что правильнее создавать исключение (за аргументам снова отсылаю к книге «Чистый код»). Если метод используется для проверки наличия пользователя, его рекомендуют переделать в форму TryX. Она неуклюжа, но уже привычна, по крайней мере, для программистов .NET: User user; if (userRepository.TryGetById(userId, out user)) { . . . } Что более ценно, она однозначна: смотря на код, вы не думаете: «а что, если такого пользователя нет?» Теперь посмотрим на ситуацию с другой стороны — а когда не надо использовать исключения На мой взгляд, тогда, когда они затрудняют понимание кода. Если ситуация не исключительная, тогда в тексте программы должна быть обычная проверка. Например, приложение для нового документа генерирует имена Untitled.foo, Untitled1.foo, Untitled2.foo и т.д. Та ситуация, что файл с таким именем уже существует, является вполне обычной, н исключительной, поэтому и реализовать код корректнее с помощью обычной проверки: public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do { filename = prefix + (++suffix).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } Этот код не только быстрее, чем аналогичный с использованием исключений, но, чт важнее, понятнее другим программистам, потому что неявно передаёт им дополнительную информацию: эта штука будет случаться регулярно, и мы к этому готовы. А вот, например, невероятная ситуация, что в папке скопилось 2 миллиарда untitled-файлов — несомненное исключение. public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do { if (suffix == int.MaxValue) throw new InvalidOperationException("You're crazy!"); filename = prefix + (++suffix).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } Такой код выглядит более запутанным. К счастью, мы можем часть проверок возложить на компилятор C#: public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do checked { filename = prefix + (suffix++).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } Откуда возникает ещё одно правило: код можно сделать чище, если знать язык, платформу, библиотеку, и опираться на их исключения. Выше я написал, что исключения выполняются медленнее, чем проверки, и хочу уточнит свою мысль: не надо опираться на производительность при принятии решения. Правильна передача смысла другому программисту, чистота кода — то, к чему следует стремится. Разница в производительности, хотя и существует, никогда не была настолько большой, чтобы пользователи её замечали. Ну, если только вы не пишите код для самого вложенного цикла в каком-нибудь графическом движке.

Ответ 2



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

Как настроить angular-gridster.js, чтобы избежать наложения изображений и их ресайза


Как настроить angular-gridster, чтобы убрать Z-слои и наложение изображений дру
на друга? Сейчас, если изображения большие, они накладываются друг на друга.

Настройки, видимо, находятся в $scope.gridOpts. И еще не срабатывает resizable: { enabled : false }, все равно можно растягивать изображения. 

Есть официальный демо-пример, там ресайз каким-то образом отключается.

Мои текущие настройки:

$scope.gridsterOpts = {
    margins: [20, 20],
    outerMargin: false,
    floating: true,
    pushing: true,
    draggable: { 
       enabled: true 
    },
    resizable: { 
       enabled: false,
       handles: ['n', 'e', 's', 'w', 'se', 'sw'] 
    }
}


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

Обновление: 
После добавления к html-блоку div, в котором находятся все изображения, опции  ресайз выключился. Осталась проблема наложения друг на друга изображений.
    


Ответы

Ответ 1



Ответ @VanyaAvchyan configuration $scope.gridsterOpts = { margins: [20, 20], outerMargin: false, floating: true, pushing: true, draggable: { enabled: true }, resizable: { enabled: false, handles: ['n', 'e', 's', 'w', 'se', 'sw'] } } view
styles .gridster-item:not(.gridster-item-moving) { z-index: initial; /* drop z-index to make it possible to set higher than 2 z-index for widget elements */ }

Работа с камерой в Android


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

UPD:
Возникла проблема с получением картинки с канваса.
Делаем так:

Canvas canvas = mHolder.lockCanvas(mHolder.getSurfaceFrame());


А что дальше - непонятно.

Про канву можно забыть. Массив с изображением мы получаем из коллбека превьюшки для камеры, но получаем всегда null и нагугливаем этот вопрос.

Использую метод так:

Camera.Size camSize = camera.getParameters().getPictureSize();
int[] outBuff = new int[camSize.width * camSize.height];
decodeYUV(outBuff, data, camSize.width, camSize.height);
Log.d(TAG, "int[]=" + Arrays.toString(outBuff));


В лог ничего не выводится.

Итого:
Нужно по тачу в превью камеры получить область изображения (в байтах). То есть, делаем тач — получаем кусок картинки на которой был сделан тач.
    


Ответы

Ответ 1



Можно использовать YuvImage: @Override public void onPreviewFrame(byte[] data, Camera camera) { Camera.Size previewSize = camera.getParameters().getPreviewSize(); YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Rect rect = new Rect(0, 0, previewSize.width, previewSize.height); yuvImage.compressToJpeg(rect, 100, baos); byte[] bytes = baos.toByteArray(); Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); // дальше можно получить область касания someImageView.setImageBitmap(Bitmap.createBitmap(bitmap, tapRegionX, tapRegionY, tapRegionWidth, tapRegionHeight)); }

Что из себя представляет null?



null - это экземпляр чего-то?
К какому типу принадлежит null?
Что такое null?
Как он представлен в памяти?

    


Ответы

Ответ 1



null - это экземпляр чего-то? Нет такого типа, которому бы соответствовал instanceof от null. 15.20.2 Type Comparison Operator instanceof RelationalExpression: RelationalExpression instanceof ReferenceType В рантайме результат оператора instanceof будет true, если значение RelationalExpression не null и ссылка может быть приведена к ReferenceType без получения исключения ClassCastException. Иначе результат будет false. Это означает, что для любого типа E и R, для любого E o, где o == null, o instanceof R будет всегда false. К какому типу принадлежит null? JLS 4.1 The Kinds of Types and Values. Есть также специальный тип - null, тип выражения null, у которого нет имени. И т.к. тип null не имеет имени, невозможно объявить переменную с типом null или привести переменную к типу null. null ссылка - единственное возможное значение выражение типа null . null всегда можно привести к любому ссылочному типу. В действительности, можно игнорировать тип null и притвориться, что null - это просто специальный литерал, который может быть любым ссылочным типом. Что такое null? Как сказано в цитате из JLS выше, можно считать, что "null - это просто специальный литерал, который может быть любым ссылочным типом". В Java null == null (что верно не для всех языков). Из описания java.lang.Object: public boolean equals(Object obj) Для любой не null переменной x, x.equals(null) должно возвращать false. null также является значением по умолчанию для всех ссылочных типов. JLS 4.12.5 Initial Values of Variables: Любой экземпляр класса, переменная или элемент массива специализируются значением по умолчанию: Для всех ссылочных типов (§4.3), дефолтное значение - null. Вы можете использовать это свойства для отложенной инициализации, когда поле буде иметь начальное значение null до тех пор, пока оно фактически не будет использовано, где будет заменено "реальным" значением (вычисление которого может быть дорогостоящим). Есть и другие применения. Если посмотреть на java.lang.System: public static Console console() Returns: The system console, if any, otherwise null. Это очень распространённая практика: null используется для обозначения несуществующего объекта. Другой пример - java.io.BufferedReader: public String readLine() throws IOException Returns: A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached. readLine() будет возвращать instanceof String для каждой строки, пока не получи null, обозначающий конец. Это позволяет обрабатывать каждую строку следующим образом: String line; while ((line = reader.readLine()) != null) { process(line); } Примечание: пустая строка - не проблема, т.к. "" != null. Давайте рассмотри java.util.Map: V get(Object key) Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. If this map permits null values, then a return value of null does not necessaril indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null. The containsKey operation may be used to distinguish these two cases. Здесь мы видим, что использование null может усложнить дело. Тут говорится, что если такого ключа нет, будет возвращён null. Второе утверждение гласит, что даже если элемент по такому ключу есть, всё равно может вернуться null. К примеру, java.util.Hashtable делает вещи проще путём запрета null в ключах и значениях так что, если V get(Object key) вернёт null это однозначно говорит о том, что под таким ключом ничего нет. Операции автоматического боксинга/анбоксинга на null выбросят java.lang.NullPointerException: Integer i = null; // при анбоксинге null в integer будет выброшен NullPointerException int a = i; Если резюмировать, то null используется как специальное значение для обозначения: Не инициализированного состояния. Терминальное условие Несуществующий объект. Неизвестное значение. Как null представлен в памяти? Из спецификации JVM: The Java Virtual Machine specification does not mandate a concrete value encoding null. Небольшое дополнение Интересная цитата C.A.R Hoare: I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. Так же можно посмотреть презентацию про ошибку на миллиард долларов.

Что такое Boilerplate code?


Читая разные статьи иногда сталкиваюсь с таким выражением :


  Boilerplate code


В моей команде внятно никто не смог объяснить это понятие. Что же это такое? 
    


Ответы

Ответ 1



В Википедии же всё написано Понятие boilerplate code или boilerplate относится к секциям кода, которые должны быть написаны во многих местах с минимальными изменениями. Часто используется по отношению к языкам, в которых программист должен написать много кода, чтобы выполнить минимальную задачу. Многие современные IDE автоматически генерируют boilerplate код. Также существуе такая вещь, как скаффолд (scaffold), который позволяет генерировать boilerplate (например, в Ruby on Rails можно генерировать базовые CRUD методы в контроллерах). Примеры: html Что такое Boilerplate code? Boilerplate code, который нам необходимо повторять в каждом шаблоне. (Пруф) JAVA public class Pet { private String name; private Person owner; public Pet(String name, Person owner) { this.name = name; this.owner = owner; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Person getOwner() { return owner; } public void setOwner(Person owner) { this.owner = owner; } } P.S.: это главная причина, по которой я в своё время отказался от программирования на Java.

Ответ 2



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

Ответ 3



Часто на хитхабе есть проекты в которых много чего сделано из того что часто встречаетс в среднестатистическом проекте, эти проекты сделали чтобы их копировли и на их основе делали свои проекты, не заморачивались с одними и теми же вещами когда начинаешь делать проект. Такой проект еще называют starter. Вот примеры: html5-boilerplate, react-boilerplate, electron-react-boilerplate, laravel-5-boilerplate, hackathon-starter. Плюсы: Многие пакеты уже поставлены и настроены Написано много кода за Вас ̶ ̶Н̶е̶ ̶т̶а̶к̶ ̶м̶н̶о̶г̶о̶ ̶к̶о̶с̶т̶ы̶л̶е̶й̶ ̶в̶ ̶к̶о̶д̶е̶ ̶к̶а̶к̶ ̶е̶с̶л̶и̶ ̶б̶ы̶ ̶п̶и̶с̶а̶т̶ь̶ ̶с̶а̶м̶о̶м̶у̶ Минусы: Бывает в стартер входит инструменты которые не любишь или не хочешь использоват сейчас, приходиться выпиливать Порой не понятно благодаря какому коду что-то работает Бывает тяжело переделать код на свой лад Нет уверенности в коде потому как писал его не сам

Словарь на C++ как (Dictionary) на C#


На C# имеется удивительно быстрый словарь (Dictionary), хотелось бы узнать, а имеетс
ли такой же производительный только на C++ ? Пробовал unordered_map, hash_map, map, но производительность в разы ниже чем у Dictionary сишарповского...
P.S: Пара имеет вид 
    


Ответы

Ответ 1



На самом деле, сравнение языков -- штука неблагодарная. Всегда найдутся тесты, н которых один из языков выиграет по сравнению с другим, и всегда найдутся люди, считающие, что данный тест не релевантен и подобный код никогда не будет встречаться в реальности. Тем не менее, я бы не сказал, что результаты ТС очень уж неожиданны: в .NET действительн выделение памяти обычно происходит быстрее, чем в нативных языках без кастомного аллокатора. А небольшие тесты обычно гораздо больше нагружают аллокатор чем, скажем, механизмы вызова функций. Причиной такой разницы в производительности аллокатора является то, что объекты C+ невозможно перемещать в памяти, а значит, привычный алгоритм выделения памяти (который как известно, поддерживает список свободных блоков, и ищет подходящий при аллокации работает довольно медленно, и, хуже того, требует глобальной блокировки памяти (что ещё более ухудшает ситуацию в многопоточном сценарии). Кроме того, объекты в C++ имеют тенденцию освобождаться быстро как только можно, что приводит к дополнительной нагрузке на освобождение памяти, которое тоже требует глобальную блокировку. В среде .NET же всё происходит по-другому. Объекты всегда выделяются на вершине heap-памяти а значит, выделение не медленнее, чем InterlockedIncrement. .NET'у не нужно поддерживать список свободных блоков потому, что при сборке мусора происходит компактификация heap-памяти: объекты перемещаются, заполняя "дыры". Кроме того, известия о том, что код на C++ вполне может быть медленнее аналогичног кода на C#, давно не новость. Вот, например, замечательная серия статей о простом приложении от мастеров нативного программирования, и резюме Джефа Этвуда: Чтобы обойти по производительности версию на C#, Реймонду пришлось написать собственны процедуры ввода-вывода, переписать класс string, воспользоваться кастомным аллокатором, а также собственной процедурой отображения кодовых страниц. Это подтверждается и бенчмарком, который приведён ниже: нативные контейнеры "из коробки" существенно проигрывают дотнетовским, (некоторые) самописные контейнеры выигрывают. Теперь самое интересное: измерения. C#: using System; using System.Collections.Generic; using System.Diagnostics; namespace Sharp { class Program { static void Main(string[] args) { var dict = new Dictionary(); int seed = 1; var timer = new Stopwatch(); timer.Start(); for (int i = 0; i < 10000000; i++) { seed = 1664525 * seed + 1013904223; dict.Add(seed, i); } timer.Stop(); Console.WriteLine( "elapsed time = {0} ms, dictionary entries count = {1}", timer.ElapsedMilliseconds, dict.Count); } } } C++: #include "stdafx.h" #include #include #include using namespace std; int main(int argc, char* argv[]) { map dict; int seed = 1; auto begin = clock(); for (int i = 0; i < 10000000; i++) { seed = 1664525 * seed + 1013904223; dict.insert(make_pair(seed, i)); } auto end = clock(); double elapsedMs = double(end - begin) * 1000.0 / CLOCKS_PER_SEC; cout << "elapsed time = " << elapsedMs << " ms, dictionary entries count = " << dict.size() << endl; return 0; } Результаты измерений (release mode, 5 запусков подряд без отладчика): C# elapsed time = 1138 ms, dictionary entries count = 10000000 elapsed time = 1127 ms, dictionary entries count = 10000000 elapsed time = 1133 ms, dictionary entries count = 10000000 elapsed time = 1134 ms, dictionary entries count = 10000000 elapsed time = 1129 ms, dictionary entries count = 10000000 C++ elapsed time = 8377 ms, dictionary entries count = 10000000 elapsed time = 8408 ms, dictionary entries count = 10000000 elapsed time = 8377 ms, dictionary entries count = 10000000 elapsed time = 8377 ms, dictionary entries count = 10000000 elapsed time = 8361 ms, dictionary entries count = 10000000 Среднее время: C# = 1132 мс, C++ = 8379 мс. Я не утверждаю, что мои тесты идеальны. Кроме того, они релевантны лишь на моём компьютере Если кто-то предложит лучшую методику измерения, я с удовольствием применю её тоже. Тем не менее, в моих условиях производительность System.Collections.Generic.Dictionary на добавление элементов существенно превосходит производительность std::map. Обратите внимание, что Dictionary использует хэш-таблицы, в то время как std::ma в моей имплементации использует красно-чёрное дерево в качестве несущей структуры данных. Хэш-таблицы обычно сами по себе быстрее, так что скорость аллокации -- не единственная причина лучшей скорости у Dictionary. Замена в C++ make_pair(seed, i) на pair(seed, i) по совету @igumnov н привела к большому отличию: 8361/8392/8361/8408/8361/8345. Замена в C++ std::map на std::unordered_map по совету @Котик привела к значительном ускорению: 2230/2230/2230/2230/2246 (среднее 2233). Тем не менее, C++ всё ещё почти вдвое медленнее. Заменил в C++ std::unordered_map на uthash по совету @igumnov. Результат немног хуже, чем std::unordered_map: 2963/2932/2948/2948/2932. Код: void testUhash() { struct myint { int key; int value; UT_hash_handle hh; }; struct myint* dict = NULL; int seed = 1; auto begin = clock(); for (int i = 0; i < 10000000; i++) { seed = 1664525 * seed + 1013904223; struct myint* ps = (struct myint*)malloc(sizeof(struct myint)); ps->key = seed; ps->value = i; HASH_ADD_INT(dict, key, ps); } auto end = clock(); double elapsedMs = double(end - begin) * 1000.0 / CLOCKS_PER_SEC; cout << "elapsed time = " << elapsedMs << " ms, dictionary entries count = " << HASH_COUNT(dict) << endl; } Добавил capacity = 10000000 в C++ и для честного сравнения в C# тоже. Изменения: C++: unordered_map dict(10000000); C#: var dict = new Dictionary(10000000); Действительно, стало скорее: C++: 1826/1856/1857/1841/1825, среднее 1841 C#: 790/786/801/790/791, среднее 792 По-прежнему C# более чем вдвое впереди. По совету @KoVadim убрал вычисление seed (capacity оставил), теперь рабочий цикл таков: C++: for (int i = 0; i < 10000000; i++) { //seed = 1664525 * seed + 1013904223; dict.insert(pair(i, i)); } C#: for (int i = 0; i < 10000000; i++) { //seed = 1664525 * seed + 1013904223; dict.Add(i, i); } Результаты: C++: 1498/1514/1498/1498/1498, среднее 1501 C#: 129/129/135/133/132, среднее 132 По совету @igumnov добавил в бенчмарк khash. Код: KHASH_MAP_INIT_INT(32, int) void testKhash() { int seed = 1; khiter_t iter; khash_t(32)* dict = kh_init(32); int dummy; auto begin = clock(); for (int i = 0; i < 10000000; i++) { seed = 1664525 * seed + 1013904223; iter = kh_put(32, dict, seed, &dummy); kh_value(dict, iter) = i; } auto end = clock(); double elapsedMs = double(end - begin) * 1000.0 / CLOCKS_PER_SEC; cout << "elapsed time = " << elapsedMs << " ms, dictionary entries count = " << kh_size(dict) << endl; } Результат: 577/577/608/577/577, среднее 583, массивный вин для нативного кода. Напомню, что лучший результат стандартного .NET-контейнера -- 792 мс. Кто предложит кастомный контейнер под .NET? Попробовал имплементацию для .NET FastDictionary (проект MapReduce.NET). Получилось немного медленнее, чем встроенный Dictionary: 853/865/842/841/842, среднее 849. Попробовал скорость чистой аллокации для проверки гипотезы @Dith: 10 миллионов раз запускается конструктор пустого класса. Код: C#: static class Allocation { class Foo { } static public void Test() { const int size = 10000000; var timer = new Stopwatch(); timer.Start(); for (int i = 0; i < size; i++) { new Foo(); } timer.Stop(); Console.WriteLine("elapsed time = {0} ms", timer.ElapsedMilliseconds); } } C++: void testAlloc() { const int size = 10000000; LARGE_INTEGER li; if (!QueryPerformanceFrequency(&li)) exit(1); double freq = double(li.QuadPart)/1000.0; QueryPerformanceCounter(&li); auto begin = li.QuadPart; for (int i = 0; i < size; i++) new Foo(); QueryPerformanceCounter(&li); auto end = li.QuadPart; double elapsedMs = double(end - begin) / freq; cout << "elapsed time = " << elapsedMs << " ms" << endl; } Результаты: C#: 58/54/28/55/55 (среднее 50) C++: 407.719/400.693/401.674/401.926/399.976 (среднее 402.4)

Ответ 2



Продолжение весьма интересной дискуссии: как сказал @VladD, дотнетовский Dictionary основан на HashMap, так вот Самая неудачная реализация функции GetHashCode в .NET Framework – это реализация, используемая по умолчанию в структурах. Дело в том, что эта функция для структур делает следующее. Она с помощью рефлексии перебирает все поля и пытается получить хэш-код. Найдя первое поле, от которого можно получить хэш-код, функция завершает свою работу, возвращая это значение. В большинстве случаев она возвращает хэш-код первого попавшегося поля. Однако если структура состоит из ссылочных типов, и все они установлены в null, то функция по аналогии с классом возвращает порядковый номер объекта. .... У такого подхода есть два недостатка. Первое – такой хэш-код может оказаться и часто оказывается некорректным, так как по одному полю тяжело идентифицировать всю структуру. Во-вторых, из-за использования рефлексии этот способ далек от идеальной производительности. Поэтому при необходимости получения хэш-значений от структур, лучше реализовать соответствующие функции самостоятельно. источник @igumnov По поводу замеров памяти, есть хорошая статья "Обработка больших объемов данных в памяти на C#"

Ответ 3



Во-первых, вспомним, что у map первый параметр - тип ключа, второй - тип значения. Вы в каждой записи добавляете ключ seed = 1664525 * seed + 1013904223; где seed = 1 постоянно. Соответственно, передается отдно и то-же значение ключа, что является наихудшим значением для вставки. Во-вторых, и самое главное. map эффективен для поиска значения по ключу и очень неэффективен при добавлении-удаления элементов. Ваш же тест делает только добавление. Основные затраты при таком тесте - не аллокация, а добавление в дереве (красно-черном, как вы правильно заметили). Если вы хотите сравнить dictionnary в дотнете и map в stl, вам необходимо написат тест, в котором сначала заполняются значения, а потом измерять время доступа к случайным значениям в цикле. Если вам в приложении нужно часто искать значения - используем map, но тогда и тес пишем другой. Если вам нужно часто и много добавлять значения - используем vector, если добавлять/удалять - list. Тогда и тест (для добавления) будет другой: vector vec; for (int i = 0; i < 1000000; i++) { vec.push_back(i); } Ваш код для 1000000 элементов выполняется у меня 8386 ms, пример с вектором - 251 мс., в 33 раза быстрее.

Ответ 4



Напишу здесь, так как мой лимит коментариев кончился Попробовал скорость чистой аллокации для проверки гипотезы @Dith: 10 миллионов раз запускается конструктор пустого класса. Этот тест показывает незнание работы компилятора и среды исполнения. В С++ кода буде утечка, в шарповом - нет, так как GC все подчистит. Это уже первый звоночек, что тесты не равнозначные. Добавили хотя бы вызов деструктора. (Но тесты показывают, что это не сильно ускорит процесс). НО! в шарповом коде по факту количество выделенной памяти не будет увеличиватьс - объект будет уничтожаться, а новый помещаться в старую память. Так как объект пустой, инициализация будет быстрая. Плюс память уже выделена. Формально - присваивание пары указателей + memset(). Но jit оптимизатор может увидеть, что объект хоть и создается, но не используется а конструктор пустой и выбросить. И по факту - гонять пустой цикл. Хотя в идеале может потом и его выбросить. Но это нужно смотреть отдельно. Я не настолько силен в C#, но думаю, что если начать выводить адрес объекта, которы создается, то он будет постоянно одним и тем же (либо там будет десяток различных адресов, которые в цикле перебираются). То есть, по факту, с++ код выглядел где то так (очень сильно упрощенный код, на коленке): Foo * f; // это глобальная переменная. for (int i = 0; i < size; i++) { // это как бы конструктор Foo * t; if (f == NULL) // если нет объекта, создадим t = alloc_memery(); init(t); else { t = f; init(t); // а это часть конструктора. Память то уже выделена, нам только поля поправить. f = NULL; // объект мы забрали } // а это деструктор. f = t; // вернули объект назад. t = NULL; } Получился такой себе пул объектов на один объект. Но можно сделать лучше, что как раз и сделает тесты более правильными. char * t = new char[1000]; // выделим себе немножко памяти. for (int i = 0; i < 10000000; i++) { new (t) Foo(); // видите странный оператор new?:) } delete[] t; (оператор new, который в коде - это такая специальная форма, называется placement new). Самое интересное - теперь утечки нет, и код работает в раз 10 быстрее оригинала Я считаю этот код максимально правдоподобным аналогом шарпового кода. Но с другой стороны, можно написать ещё красивее for (int i = 0; i<10000000; i++) Foo f(); // да, здесь будет создаваться объект и вызываться конструктор-деструктор не удивлюсь, если шарповый оптимизатор также сделал в оригинальном коде выделени памяти на стеке (это возможно, я точно знаю, что 7 java так умеет). А выделение память на стеке очень дешевое в плане скорости. Кто то скажет, во в C# прям взял написал и оно быстро, а в С++ нужно ещё что то добавлять Но в С++ "хаки" делаются легко и не принужденно, а в C# можно быстро напороться на препятстви (да, я знаю об Unsafe коде). Но приведенный выше код я хаком не считаю. И я скорее всего не буду создавать с помощью new миллионы объектов в цикле. А если и понадобиться, то сделаю преаллокацию на нужное кол-во объектов. И это будет быстрее.

В чём смысл и преимущества #!/usr/bin/env?


Этак с начала времён (юниксовых, т.е. 1.01.1970) в начале скрипта рекомендовалос
использовать shebang / hashbang - строку, указывающую на используемый интерпретатор, например:

#!/bin/bash
echo 'hello world'

#!/usr/bin/python
print 'hello world'

#!/usr/bin/python3
print('hello world')


Недавно наткнулся на рекомендацию использовать вместо этого такую форму:

#!/usr/bin/env bash
echo 'hello world'

#!/usr/bin/env python
print 'hello world'

#!/usr/bin/env python3
print('hello world')


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


Ответы

Ответ 1



Основная идея - улучшение переносимости. Не гарантируется, что на различных системах исполняемый файл будет лежать по пути, который указан в shebang. Использование env позволяет снизить этот риск за счет запуска команды на основе данных из переменной среды PATH Более того, если по каким-либо причинам вместо стандартного исполняемого файла пользовател хочет использовать свой, то ему достаточно добавить путь к этому файлу в PATH без необходимости исправления скриптов: ~ $ cp /bin/bash /home/soon/python ~ $ export PATH=/home/soon:$PATH ~ $ env python [soon@archlinux ~]$ exit В примере выше я скопировал bash к себе в домашнюю директорию (переименовав при это файл в python), добавил путь в PATH и запустил python с помощью env, которая усужливо запустила bash, т.к. нашла его раньше. Еще одним примером является использование виртуальных окружений при разработке н Python (virtualenv). Поскольку они также перебивают PATH, env позволяет использовать нужную версию исполняемого файла: ~ $ workon black-box-challenge-2016-py2 ~ (venv:black-box-challenge-2016-py2) $ env python Python 2.7.11 (default, Mar 31 2016, 06:18:34) [GCC 5.3.0] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> print sys.executable /home/soon/.virtualenvs/black-box-challenge-2016-py2/bin/python >>>

Ответ 2



краткое резюме из информации, приведённой в ответах к этому вопросу и к аналогичным вопросам: Why is it better to use “#!/usr/bin/env NAME” instead of “#!/path/to/NAME” as my shebang? Why is #!/usr/bin/env bash superior to #!/bin/bash? преимущества: будет запущена программа не из конкретно указанного файла, а из того, что встретитс первым в списке каталогов, заданных переменной окружения PATH пользователя, запустившего скрипт. полезно для случаев, когда по указанному пути в системе, на которой будет выполнятьс скрипт, такого файла нет, или когда пользователь модифицирует эту переменную окружения для того, чтобы выполнялась какая-то особая программа (другая реализация, другая версия). недостатки: будет запущена программа не из конкретно указанного файла, а из того, что встретитс первым в списке каталогов, заданных переменной окружения PATH пользователя, запустившего скрипт. например, разные пользователи (или тот же пользователь, но с модифицированным содержимы этой переменной окружения) могут получить разные результаты выполнения одного и того же скрипта. программы /usr/bin/env может не существовать в системе, где будет запускаться скрипт, соответственно, попытка запуска будет неудачной. нельзя будет указать дополнительной опции для выполняемой программы. так получится: #!/путь/к/программе опция а так — нет: #!/usr/bin/env программа опция

Ответ 3



Это связано с переносимостью скриптов. Дело в том, что путь к python, к примеру на разных системах может отличаться, а вот путь к env на всех системах неизменный. Поэтому вызывая env и передавая ей в качестве аргумента нужный интерпретатор, можно быть уверенным, что скрипт будет запущен вне зависимости от того, где на самом деле находится интерпретатор (главное, чтобы он был в PATH).

Ответ 4



Команда env отображает текущие переменые окружения. А если стоит с командой env bash то выполняет команду с текущеми перемеными окружения. В некоторых системах при запуске интерпритаторов используется не текущие перемены окружения , а считываются из файлов или даже ставятся по умолчанию. И если Вы изменили или добавили переменые окружения , то они не будут учтены в запускаемом скрипте, а при запуске через env все переменые будут учтены.

Почему проблема порядка байтов есть в UTF-16, но её нет в UTF-8?


Я прочитал, что в UTF-16 два различных порядка байтов (endianness) появились потому, что два различных порядка байтов существуют в архитектуре процессоров: 


  Систему, совместимую с процессорами x86, называют little endian, а с процессорами m68k и SPARC — big endian.


То есть одно и то же число 0x1234ABCD кодируется последовательностью байтов:


little endian: 12 34 56 78
big endian: 78 56 34 12


Соответственно, при раскодировании последовательности байт в последовательность чисе
(или code point'ов юникода) нужно учитывать использованный при кодировании порядок байтов. (Это несколько дилетантское утверждение, но лучше сформулировать я пока не могу).

Например, если мы кодируем "Привет 😃" в UTF-16:

# big endian:
П      р      и      в      е      т      ( )    😃
04 1F  04 40  04 38  04 32  04 35  04 42  00 20  D8 3D DE 03

# little endian:
П      р      и      в      е      т      ( )    😃
1F 04  40 04  38 04  32 04  35 04  42 04  20 00  03 DE D3 D8


Вроде бы всё очевидно. Мы сопоставляем code point'y некоторое число согласно алгоритм
кодировки, а потом записываем это число в соответствии с порядком байт, принятым в системе.

Теперь UTF-8:

П      р      и      в      е      т      ( ) 😃
D0 9F  D1 80  D0 B8  D0 B2  D0 B5  D1 82  20  F0 9F 98 83

# в двоичной системе счисления:
11010000 10011111 
11010001 10000000 
11010000 10111000 
11010000 10110010 
11010000 10110101 
11010001 10000010 
# по первому биту сразу видно, что этот code point закодирован одним байтом
00100000 
# а здесь первый байт начинается с 4 единиц, значит будет 3 trailing byte'а
11110000 10011111 10011000 10000011


Алгоритм кодировки поменялся, но архитектура процессора осталась прежней! Мы по прежнем
получаем число, которое занимает от 1 до 4х байт. Почему с UTF-8 нас не беспокоит, что байты будут записаны вот так?

П      р      и      в      е      т      ( ) 😃
9F D0  80 D1  B8 D0  B2 D0  B5 D0  82 D1  20  83 98 9F F0




Дополнение:

Задавая этот вопрос, я уже знал, что UTF-8 использует однобайтовые code unit'ы, а UTF-16 – двухбайтовые. Попробую уточнить, что мне было непонятно.

Есть символ «😃». При кодировании его в алгоритме UTF-8 получается последовательност
байт F0 9F 98 83. Это тоже число, четырёхбайтовое слово, его можно использовать дл
сравнения или сортировки строк, закодированных в UTF-8 (правда, толку от такой сортировк
немного). В вышеуказанном виде оно имеет порядок big-endian, значит системы с архитектурой big-endian могут получить преимущество в работе с ним. Но что с little-endian? Как там будет происходить сравнение? Для примера, будем сравнивать «😃» (F0 9F 98 83) и «😐» (F0 9F 98 90). У меня есть два предположения:


Big-endian системы работают с закодированными в UTF-8 символами, как с 1, 2, 3, 4-байтным
словами и получают преимущество в скорости операций. То есть, в них достаточно сравнить F09F9883 и F09F9890 как четырехбайтовые слова. Little-endian системы вынуждены сравнивать побайтно или переворачивать слово дважды.
Любая архитектура работает с закодированными в UTF-8 символами строго как с последовательностям
байт, не оперируя словами более 1 байта. То есть, сравниваются пары байт: FO и FO, 9F и 9F, 98 и 98, 83 и 90. При этом теряется потенциальное преимущество от сравнения двух слов, зато для любой архитектуры алгоритм работает одинаково.

    


Ответы

Ответ 1



Дело в том, что UTF-8 и UTF-16 обычно хранится в памяти нераспакованным, в том ж виде, как он приходит в потоке (например, в файле). [Ну и если он таки распаковывается, то это рассмотрение играет роль в момент распаковки.] Само по себе хранение никакой проблемы, понятно, не создаёт. Проблему создаёт обработка, например, сравнение символов. В UTF-8 вы читаете входной поток по байту, и интерпретируете их последовательно Соответственно получившееся значение code point получается однозначно и не зависит от порядка байтов машины: результат приведения к code point однозначно определён, и при сравнении используется именно он. А вот в UTF-16 вы читаете входной поток по два байта, и для сравнения в обычном случа вовсе не нужно вычислять code point. Если у вас есть двухбайтное слово в нативной кодировке не соответствующее суррогатной паре (а это основной, самый частый случай), то для сравнение можно просто использовать её значение, она равна своему code point. Но если кодировка не нативная, вам понадобится переставить байты. Если бы в UTF-16 был задан конкретный порядок байт, составляющих двойной байт ( тем самым задана endianness), то платформы, на которых данный порядок не является нативным оказались бы в проигрыше: они должны были бы совершать дополнительные действия (перестановку байт) при чтении и записи потока. С двумя вариантами кодировки приложения могут пользоваться тем форматом, который нативен на их платформе, получая тем самым выигрыш в скорости. Держать байты в памяти в ненативном порядке — плохая идея: их получается намног затратнее сортировать и сравнивать. С нативным порядком в обычном случае нужна лиш проверка на суррогатную пару, а с ненативным ещё и перестановка байтов. Например, для сравнения 1C 55 и 1B 77 в big endian-смысле на little endian-системе не обойтись без перестановки байт. Потому что если сравнивать без перестановки, то будут сравниваться 0x551C и 0x771B, и результат будет неверным. То же и для сортировки. Обновление ответа к обновлению вопроса. Насколько я понимаю, при обработке UTF-8 мы не знаем наперёд, сколько байт буде занимать тот или иной символ. Поэтому мы вынуждены работать с потоком байт, а не потоко нативных слов. Если бы мы знали, что наш символ всегда кодируется четырьмя байтами мы могли бы или просто сравнить нативным образом,или для неподходящей байтоориентаци скопировать оба четырёхбайтных слова во временные переменные, развернуть их и сравнить нативным сравнением четырёхбайтных слов. Но этому мешает ещё и то, что наши четыре байта находятся на случайной позиции в потоке, и значит, скорее всего не выровнены на границу 4-ёх байт. На многих архитектурах (кроме, однако, x86) такой доступ не разрешён, и придётся «выковыривать» байты по частям. Таким образом, получается проще и эффективнее просто сравнить байты по одному. В UTF-16, кстати, возможных случаев меньше: там возможен либо символ из одного codepoint'а который можно сравнивать нативно или с одним разворотом, если не угадали с порядком байт, либо из двух (где наверное лучше снова-таки сравнить два раза по двухбайтному слову).

Ответ 2



Почему проблема порядка байтов есть в UTF-16, но её нет в UTF-8? Потому что code unit равен 8 битам (одному байту) в UTF-8 и 16 битам (двум байтам в UTF-16. В зависимости от порядка байт внутри code unit, есть utf-16le и utf-16be кодировки, которые могут быть использованы на одном и том же компьютере вне зависимости от endianness CPU): >>> 'я'.encode('utf-16le').hex() '4f04' >>> 'я'.encode('utf-16be').hex() '044f' Символ я (U+44f) кодируется в UTF-16 в одно и то же 16-битное число: 1103 == 0x44f что для utf-16 совпадает с номером (Unicode code point) символа в Юникоде (для BMP символов). Само 16-битное число может быть в памяти представлено в виде двух байт: 4f 04 (от младшего к старшему порядок байт) или 04 4f (от старшего к младшему порядок байт). >>> 'я'.encode('utf-8').hex() 'd18f' я (U+44f) кодируется в UTF-8, используя два 8-битных числа 209 == 0xd1 и 143 = 0x8f. В общем случае UTF-8 может использовать от 1 до 4 октетов (8-битных чисел) для каждого символа (Unicode code point). >>> '😂'.encode('utf-16le').hex() '3dd802de' >>> '😂'.encode('utf-16be').hex() 'd83dde02' >>> '😂'.encode('utf-8').hex() 'f09f9882' Символ 😂 (U+1f602) кодируется в utf-16, используя два 16-битных слова (utf-16 cod units): 0xd83d и 0xde02 (utf-16 суррогатная пара). Представление слова в виде байт зависит от выбранного порядка байт (le, be), но порядок самих слов не меняется. 😂 (U+1f602) кодируется в utf-8, используя четыре октета (utf-8 code units): 0xf0 0x9f, 0x98, 0x82. Представление октета в виде 8-битового байта, очевидно, не зависит от порядка (один октет—один байт). Последовательность code units (октеты для utf-8 и 16-битные слова для utf-16), используема для кодирования выбранного символа, однозначно определена выбранной кодировкой—в частности нельзя порядок code units менять как в utf-16 так и в utf-8 кодировках. Оба пункта из дополнения к вопросу у вас неверны. Не нужно путать как результат виде байт представляется при обмене с внешним миром или внутри разных частей в программ (при записи на диск, отсылке по сети, вызове API) и какие инструкции CPU используе для работы с данными, выполняя конкретный алгоритм. То что октеты нельзя переставлять в utf-8 результате, ещё не значит, что фактические алгоритмы не могут работать с бо́льшими единицами. К примеру, memcpy() очевидно сохраняет порядок байт, при этом фактическая реализация может работать c целыми словами (например, с 64-битными словами).

Ответ 3



Сама по себе проблема с endianness возникает из-за разной общепринятой визуализации значений в памяти в и регистрах процессора: Вот как обычно нумеруют байты в регистре процессора: [ 12 34 AB CD ] разряд 3 2 1 0 Старшие разряды пишутся слева - как традиционно сложилось в математике. В то же время в той же математике сложилась традиция рисовать оси и отрезки и прочи множества слева направо. И все представляют себе память как длинный бесконечный массив байт. [ ?? ?? ?? ?? ...] адрес 0 1 2 3 И у разработчиков процессора есть два решения: сохранять 0-й разряд в 0-й адрес, 1-й в 1-й и т.д, пожертвовав визуализацией, н получив выигрыш в скорости и простоте операций типа "вычитать из памяти 0-й байт в 0-й разряд (little-endian)" разворачивать значение при сохранении, ради удобства отладки В рамках одной системы нет никаких проблем. Главное писать значения в память та же, как вы их оттуда читаете - и можно не думать об endiannes. И все идет хорошо, пока вам не надо передать строку на другой компьютер (прямо по сети, или косвенно - как файл). Создатели сетевых протоколов заранее договорилис как передавать отдельные байты. Т.е. если вы передадите с x86 по сети байты 1, 2, 3, 4, 5, 6 - то любая моторолла получит их по сети в порядке 1, 2, 3, 4, 5, 6. Это жестко вбито в стандартах, на всех уровнях, от TCP/IP до Ethernet. А вот насчет передачи пар байт или четверок байт - никаких договоренностей нет. UTF-8 работает с потоком байт. Предположим, вы хотите записать на диск или переслать "привет" по сети. Это, с точки зрения энкодера, выглядит так: передать D0 передать 9F передать D1 передать 80 ... Получающая сторона (стандарт же!) гарантированно прочитает их в том же порядке D0 9F D1 80... Опять же, при записи в память по одному байту никаких разворотов не происходит, и в памяти это же значение представляется в виде [ D0 9F D1 80 ] Так происходит только потому, что в русском (английском) языке принято писать буквы направо, что случайно совпадает с общепринятой визуализацией памяти. Поэтому строку UTF-8 достаточно просто записать в память - и она готова к передаче Это результат договоренностей на уровне сетевых протоколов / протоколов общения с диском - они тоже работают на уровне байт, а не на уровне бит. Ок, теперь нам захотелось передать тот же привет, но в UTF-16: 0x041F 0x0440 0x0438 0x0432 0x0435 0x0442 Энкодер в UTF-16 не заморачивается и передает эти двухбайтовые слова по сети/в память/н диск. По слову за раз. И ожидает что первое слово будет передано/записано первым, второе - вторым и т.д. передать 0x041F передать 0x0440 передать 0x0438 Как они будут записаны / переданы - зависит от endiansess. Для LE процессор не заморачивается [ 1F, 04 ] [ 40, 04 ] [ 38, 04] адрес 0 1 2 3 4 5 Для big endian - он старательно разворачивает каждое слово: [ 04, 1F ] [ 04, 40 ] [ 04, 38] адрес 0 1 2 3 4 5 Договоренностей о передаче двухбайтовых слов по сети нет, как и договоренностей о хранении их на диске. Поэтому в UTF-16 требуется BOM. На самом деле та же проблема существует с битами при использовании UTF-8 (да и вообщ при передаче любых байт куда угодно). Например, вы хотите передать по сети байт D0. Он же 11010000. Вы будете передавать его как 0, 0, 0, 0, 1, 0, 1, 1? Или как 1, 1, 0, 1, 0, 0, 0, 0? Вы с этой проблемой не сталкиваетесь по разным причинам: - Отсутствует необходимость визуализации бит при хранениии. - Закрыт доступ к реальному формату хранения на физическом уровне (память и дис не позволяют адресовать и читать отдельные биты). - Жесткая стандартизация - заранее согласованный порядк передачи бит в рамках байта в конкретном сетевом протоколе позволяет вам работать на уровне байт. Достаточно взять любой способ передачи, в котором насчет порядка бит виден (например, попробуйте сделать железку, которая через COM-порт) - и проблема себя проявит.

Ответ 4



То есть одно и то же число 0x1234ABCD кодируется последовательностью байтов: little endian: 12 34 56 78 big endian: 78 56 34 12 Соответственно, при раскодировании ... Нет никакого раскодирования. Есть два представления числа в памяти. http://ideone.com/wsOkXK #include int main() { volatile int x = 0x1234ABCD; const unsigned char *p = (unsigned char *)&x; printf("%0*X\n", sizeof(int) << 1, x); for (unsigned q=0; q

Ответ 5



В обоих случаях порядок выборки из памяти чисел, кодирующих символ, одинаковый. случае UTF-8 эти числа однобайтовые и проблемы старшего и младшего байтов, по понятно причине, не возникает. Когда же речь заходит об UTF-16, оказывается, что в разных архитектурах порядок записи байтов в память разный. Например, в архитектуре Intel первым идёт младший байт, а в ARM - старший.

В чём смысл уязвимости Meltdown?


Недавно стало известно об уязвимости Meltdown, которая затрагивает большинство процессоро
Intel. В чём она заключается? Как функционирует атака? Как можно сдетектировать атаку и/или защититься от неё?
    


Ответы

Ответ 1



Смотрите, в чём дело. У многих современных процессоров есть так называемое спекулятивное выполнение. Что это такое? Дело в том, что компьютерная память медленная, а выполнение инструкций быстрое. Поэтом если условный переход зависит от значения, лежащего в памяти, то процессор во врем ожидания, пока из памяти «подгонят» нужное значение, может начать выполнять инструкци той ветки, которую он считает более вероятной. Эти инструкции выполняются «секретно», их результаты не видны до тех пор, пока не произойдёт реальная подгрузка ожидаемого значения из памяти и процессор реально не убедится, что выполнение таки-пойдёт по ожидаемой ветке. Если выполнение реально пойдёт по нужной ветке, побочные эффекты «секретно» выполненны операций публикуются процессором и становятся видимыми другим инструкциям. Если же выполнение не пойдёт по нужной ветке, спекулятивно выполненные команды аннулируются, и их результат никому не виден. Однако, эти отменённые команды могут всё же производить побочные эффекты, наблюдаемы из нашего кода. Если отменённая команда читала память, которая не была загружена в кэш процессора, то эта память грузилась в кэш процессора, и последующее обращение к ней было быстрее. Таким образом, можно судить о том, что же делала отменённая команда. Каким образом можно это использовать? Мы можем в спекулятивно выполняемом коде зависимости от считанных данных подгрузить в кэш различные куски памяти, и измерив время доступа к ним, решить, какие же данные были прочитаны. Чем это отличается от обыкновенного чтения данных? Вот тут-то и проявляется ошибк в Intel'овских процессорах. Дело в том, что мы можем спекулятивно читать память ядра, которая нам недоступна. Ошибка заключается в том, что при спекулятивном чтении не проверяются права на доступ к памяти. Итак, наш код выглядит следующим образом: Мы выделяем участок памяти X, по длине больший, чем несколько линий кэша. Мы сбрасываем кэш так, чтобы память X не была в кэше. (Например, так.) Мы определяем адрес в ядре, который мы хотим прочитать, и записываем его в указатель p. Мы пишем код вида if (data == 0) { byte kernelByte = *p; byte dummy = X[(kernelByte & 1) * CACHE_LINE_SIZE]; } В data мы кладём ненулевое значение, и вытесняем data из кэша. Мы выполняем указанный код, после чего измеряем время доступа к X[0] и X[CACHE_LINE_SIZE]. Что происходит? Весь код внутри if не выполняется в реальности, но выполняется лиш спекулятивно. Про спекулятивном выполнении чтение по указателю на память ядра завершаетс успешно. В зависимости от значения младшего бита спекулятивно прочитанного значения мы производим доступ к одной из двух кэш-линий. Таким образом, одна из кэш-линий окажется загруженной в результате спекулятивного выполнения. Если время доступа к X[0] близко к типичному времени доступа к закэшированной памяти то в спекулятивном коде было обращение к X[0], и значит, kernelByte & 1 == 0. Если врем доступа к X[CACHE_LINE_SIZE] близко к типичному времени доступа к закэшированной памяти, то в спекулятивном коде было обращение к X[CACHE_LINE_SIZE], и значит, kernelByte & 1 == 1. Таким образом, мы смогли прочитать один бит защищённой памяти. Далее, один за одни можно считывать информацию. На текущий момент скорости считывания составляют около 1-2 килобайт в секунду. Процессоры AMD не подвержены атаке Meltdown. Если верить информации от инженера AMD, AMD processors are not subject to the types of attacks that the kernel page table isolation feature protects against. The AMD microarchitecture does not allow memory references, including speculative references, that access higher privileged data when running in a lesser privileged mode when that access would result in a page fault. Параллельно с описанной атакой Meltdown есть очень похожая, в некотором смысле более общая атака, известная как Spectre. В ней «недоступная» память читается так же, но не через границы уровней доступа То есть, читается не ядро, а память собственного процесса (или, возможно, даже другог процесса, бегущего с теми же правами!). Этой уязвимости подвержены уже и процессоры AMD, потому что блокировка спекулятивного выполнения на уровне процессора «не видит» границ между процессами, т. к. процесс — понятие уровня операционной системы. Зачем может понадобиться читать память своего же процесса? Например, для выхода и «песочницы». Код на JS в браузере обычно бежит в том же процессе, с теми же правами а контроль над тем, чтобы JS не читал память за границами положенного, возложена на код, который проверяет выход индексов за границы диапазона. Как мы уже видели, выход индекса за границы диапазона возможен даже при контроле значения индекса при спекулятивном исполнении кода. Обе атаки полностью пассивные, «молчаливые», поэтому на текущий момент не существуе защиты или метода обнаружения атак, который мы как пользователи смогли бы использовать. Единственный способ защититься от Meltdown — ждать патча операционной системы. Патч для Meltdown заключается в том, что из виртуального пространства процесса операционная система убирает память ядра. Это замедляет системные вызовы, но исправляет данную проблему. Майкрософт выпустила патч против Meltdown для Windows 10 3.01.2018 как KB4056892 Убедитесь, что он у вас поставлен (если у вас, конечно, Windows 10). Это можно сделать, например, при помощи PowerShell: PS > Install-Module SpeculationControl PS > Get-SpeculationControlSettings Apple включила защиту от Meltdown в iOS 11.2, macOS 10.13.2 и tvOS 11.2. Проверьте вашу версию системы, если у вас Мак или i-устройство! Обновления для Ubuntu практически готовы, и ожидаются 9.01.2018 (или раньше, если получится). Не пропустите их. Одна из неприятностей данных уязвимостей состоит в том, что атаку при определённых условиях можно проводить даже из Javascript. Чтобы защититься от атаки Meltdown из javascript'а других сайтов, можно его временн отключить до выхода патча, но вы же не станете этого делать? Патча на Spectre для операционной системы нет, и он невозможен: уязвимость нужно исправлять в браузере. Производители браузеров тоже прилагают усилия, чтобы было сложнее воспользоваться уязвимостью из Javascript. Firefox включает изменения, направленные на борьбу с уязвимостью, начиная с уже вышедшей версии 57, и усиливает защиту в версии 57.0.4 (бета). Хром выпустит изменения в JS-движке V8 23 января (версия 64), но уже начиная с сегодняшнег дня будет предупреждать об использовании подозрительного API. На сегодняшний день Гугл рекомендует поставить последнюю версию Хрома и включить Site Isolation. Edge и Internet Explorer получили изменения, направленные на борьбу с использованием уязвимости, начиная с уже вышедшего обновления KB4056890. Улучшения браузеров мешают JS-коду получить точное время для измерения времени выполнени участков кода. Это, к сожалению, не закрывает полностью атаку Spectre, потому что изобретательный взломщик может найти способ измерять время другими методами. Поэтому Spectre, на эмблеме которого изображено привидение, будет ещё долго появляться и пугать нас. Небольшое обновление: в выпущенной сегодня (26.01.2017) версии Visual Studio компилято C++ включает новый ключ /Qspectre, который защищает скомпилированные программы от утечки данных через Spectre. Официальное объявление здесь.) Литература: Project Zero, Reading privileged memory with a side-channel Wikipedia: Meltdown Geektimes: Новогодние подарки, часть первая: Meltdown Отдельное спасибо участникам @PashaPash и @Arhad, обсуждение с ними очень помогло прояснить общую картину и яснее понять проблематику.

Ответ 2



В чём она заключается Внимание: текст ниже является донельзя утрированным и однобоким описанием работ процессора, в котором опущены многие важные детали. Однако это вынужденная мера для упрощения понимания сути атаки. 1. Многопоточность там, где её не ждут Все относительно современные процессоры выполняют данные не последовательно, команд за командой, а параллельно, кучей команд за раз. Это называется суперскалярная архитектура. Однако любой параллелизм ломается ветвлениями (условиями, циклами и заходами в функции) Процессор не телепат, а потому не знает, куда именно пойдёт выполнение на очередно развилке. Поэтому он отслеживает, куда выполнение идёт чаще всего, и упреждающе помещает в параллельное исполнение команды именно этой ветки. Это называется спекулятивное исполнение. Но процессор может и ошибаться, а потому все операции с регистрами и памятью в это ветке перенаправляются во временное хранилище, так называемые временные регистры, содержимое которых записывается по назначению только при удачном переходе по ожидаемой ветке. В противном случае их содержимое отбрасывается. Иными словами, весь выполняемый на упреждение код как бы сидит в «песочнице». И во тут инженеры Intel допустили оплошность с целью оптимизации. Мол, если запись при упреждающе выполнении невозможна до известного процессору момента, то и все проверки безопасности можно отсрочить до того же момента — всё равно в случае чего данные будут бесследно отброшены. Если бы инженеры знали, как они жестоко просчитались насчёт понятия «бесследно» Да, временные регистры абсолютно изолированы. Но у инструкций, выполняемых на опережение, имеется доступ не только к регистрам, но и к такой долгоживущей штуке, как кэш. 2. Сага о кэше Кэш — это место, куда процессор временно складывает копии фрагментов оперативно памяти. Зачем он это делает? Всё просто. Оперативная память — штука крайне медленная требующая сотни тактов на чтение одного байта. Потеря сотни тактов на каждый чих — непозволительна роскошь, да и из всей оперативной памяти программе, как правило, требуется совсем немного. Поэтому всю память условно разбивают на череду блоков по 64 байта каждый, а процессор снабжают небольшим (32КБ) количеством сверхбыстрой памяти, в которую он копирует те из блоков, что пытается читать программа. Блочность используется в предположении, что программа будет не скакать по оперативной памяти, а располагать данные компактно. Вроде бы всё понятно и очевидно. Ах, да, кэш можно выборочно (поблочно) очищать дл возможности получения наисвежайших данных из оперативной памяти. И ещё, если нужного блока в кэше ещё (либо уже) нет, команда-инициатор обращения приостанавливается и ждёт свои сотни тактов, пока блок не будет готов. 3. В гостях у проктолога, или используем стандартные средства нестандартным способом Вы ещё не заметили ничего необычного? Значит вы не хакер (в исконно-благородном смысл этого слова). Ведь если принять каждый блок в кэше за один бит, а его пустоту/полнот за ноль и единицу соответственно, то мы получим прекрасное хранилище данных. Кратковременное, конечно, — до близжайшего массового сброса переключением задач, — да и объём подкачал. Но всё равно оно прекрасно. Вы спросите: чем? Да тем, что к этому «хранилищу» имеют доступ инструкции из опережающег выполнения, упомянутого в начале (далее ОП)! То есть получается следующая воистину гениальная картина: ОП, с одной стороны, имеют неподконтрольный и неограниченный доступ ко всей оперативно памяти (включая память ядра ОС, лежащую в верхнем диапазоне адресов). Ведь, как был сказано выше, все проверки будут выполнены позднее, когда настанет черёд проверки взятия условного перехода и записи изменений в регистры и память. А пока за нами никто не следит, мы с ОЗУ наедине — спасибо вам, инженера Intel! С другой стороны, ОП имеют доступ к кэшу. Записать туда, конечно, ничего нельзя (эт кэш чтения, а не записи), но можно сделать из него вышеописанное «хранилище» (заране сбросив его) и производить чтение из определённых блоков памяти, отправляя их в кэш (мы условились, что это запись единичных бит в хранилище). С кэшем мы тоже наедине — ведь выполнение опережающее, а потому нас пока как бы и нет. То, что нас потом, возможно, отбросят, неважно — мы уже успеем поиграть с кэшем. А на стыке этих двух вещей рождается поистине грандиозная атака по побочным каналам позволяющая втихаря читать любую память, включая привелегированную, и отправлять её содержимое наружу. Осталось решить только две проблемы: как впоследствии читать из такого «хранилища», как быть с посланным вдогонку прерыванием о нарушении страничной защиты; ведь неизбежн настанет момент сохранения временных результатов, при котором и вскроется наше переступление границ памяти. 4. Практическое использование Первая проблема решается достаточно легко. Для этого достаточно пробежаться по все той памяти, которой соответствуют использованные для «хранилища» записи кэша. Те блоки которые не были тронуты (то есть как бы обозначают ноль) будут считываться значительн медленнее (помним про сотни тактов?), чем те, которые были принудительно предзагружены (то есть обозначают единицу). Если время доступа вообще неприличное, значит у нашего процесса истекло время исполнения и его вытеснили другим — повторяем запрос. Ну а точное время выполнения команды можно узнать у самого процессора с точностью до такта, благо есть соответствующая ассемблерная команда. А вот у второй проблемы есть целых два решения, которые и известны как те самые Meltdown и Spectre: Можно вообще плюнуть на исключение, благо степень параллелизма у современных процессоро большой, и мы спокойно (если повезёт) успеем прошерстить блоки памяти и восстановить переданное число. Это Meltdown. Можно обмануть процессор, пустив выполнение по неожиданной для него условной ветви Тогда исключения не возникнет в принципе, так как всё исполнение будет ошибочно-излишним. Ну а про побочный эффект где-то в районе кэша никто никогда и не узнает. Это Spectre. Вот и вся суть обеих нашумевших уязвимостей. Кстати, это не гипотетические изыскания а практичные вещи, с использованием которых исследователи спокойно копировали ядерную память стабильным потоком в пару килобайт в секунду. Как защититься Если вы пользователь — то никак. Уязвимость слишком низкоуровневая. Если вы разработчик программ или операционных систем — сбрасывайте участки кэш-памяти с чувствительными данными перед передачей управления стороннему коду. Если вы разработчик микрокода в компании Intel — перенесите все проверки правомерност доступа с этапа применения команд на этап опережающего исполнения, как уже давно сделано у AMD. И не тяните с выпуском обновления микрокода.

Ответ 3



Найдено на самом деле две разных уязвимости с похожим механизмом но с совершенн разными последствиями: Meltdown - Rogue Data Cache Load Подвержены Intel и некоторые из ARM. Позволяет читать память ядра из юзермода. Прикрывается только патчем на OS, которы перестает мапить память ядра, что бьет по производительности (и вызывает промахи кэша при системных вызовах). Позволяет процессу без ограничений читать память других процессов. Позволяет коду в одной виртуальной машине читать память хоста и других виртуальных машин. Последнее - и есть основная проблема. Затронуты все хостеры, включая Amazon и Azure. Иcправления: Windows KB4056892 Linux - ядра 4.14.11, 4.9.75 и будущее 4.15. MacOS - 10.13.2/10.13.3 Никакое "отключение JS" не помогает. Апдейта микрокода не будет. Единственный вариан - патч на операционку, который достаточно сильно бьет по производительности на IO-intensive workload (сеть и базы данных). В играх - никакой разницы. Долгосрочные последствия: Интел выпустит новые исправленные процессоры, на 30% быстрее которые раскупят намного быстрее, чем 8-е поколение. Хостеры, предоставляющие VM на исправленных процессорах, поднимут цены. Spectre - Bounds Check Bypass + Branch Target Injection Подвержены Intel, AMD и ARM. Позволяет обойти проверки вида if (x < array1_size) y = array1[x]; при доступе массиву в рамках одного процесса, выполняя код в том же процессе. Основная проблема: позволяет коду на JavaScript обойти защиту и читать всю память процесса-браузера. Исправления ожидаются со стороны уязвимых приложений: Firefox v57. Хром v64. Edge и Internet Explorer - KB4056890. Исправления браузеров никак не отключают сам Branch Prediсtion. Они снижают точност performance.now и временно отключают SharedArrayBuffer (был использован как таймер авторами атаки для Chrome). И, наверное, переписывают подверженные уязвимости проверки. Долгосрочные последствия: Пострадают, прежде всего, разработчики браузеров. Всем кто запускает в своем процессе сторонний код, придется очень аккуратно бороться с возможностью прочитать память всего процесса.

Код-гольф: Увеличиваем цифры


Задача: Написать код минимально возможной длины, выводящий полученную на вход, строк
из цифр большим, символьным шрифтом. Шрифт должен быть в точности такой, как указан тут, в вопросе.

Пример:

На вход получена строка "0123456789", на стандартный вывод программа должна вывести:

  ###      #     #####   #####  #       #######  #####  #######  #####   #####
 #   #    ##    #     # #     # #    #  #       #     # #    #  #     # #     #
#     #  # #          #       # #    #  #       #           #   #     # #     #
#     #    #     #####   #####  #    #  ######  ######     #     #####   ######
#     #    #    #             # #######       # #     #   #     #     #       #
 #   #     #    #       #     #      #  #     # #     #   #     #     # #     #
  ###    #####  #######  #####       #   #####   #####    #      #####   #####


Правила и ограничения:


Программа может, но не обязана быть оформленной в виде функции. Если    она функци
- синтаксис объявления этой функции (int main() {} для C)    не учитывается в размере, важен размер самого рабочего кода.
Входная строка может поступать в программу любым, удобным вам, способом: в виде переменной
указанной в тестовом примере непосредственно перед кодом, в виде параметра функции или со стандартного ввода.
Входная строка может содержать только цифры
Результат должен быть выведен на стандартный вывод в текстовом виде. Если стандартны
вывод направлен на терминал, экран считать достаточной ширины для вывода всего контрольног
примера c запасом. Переход на новую строку обозначайте (явно или не явно) любым символом/комбинацией символов, использующейся для перевода строки на вашей платформе (Например, \n или \r\n).
Шрифт результата должен в точности соответствовать указанному выше. Между цифрам
на выводе должен быть минимум один пробел (цифры не должны сливаться). Шрифт считается моноширинным, т.е. вокруг единицы может быть больше пустого пространства т.к. ее изображение ýже остальных цифр.
В программе запрещено использовать любые встроенные в язык и библиотеки к нему функции сжатия и кодирования данных (Такие как: zip/unzip, base64)
Программа должна содержать шрифт (или код его формирующий) непосредственно в свое
теле. Получать шрифт из внешних источников (ввод, диск, сеть, память видеоадаптера, bios) запрещено.
Размер программы учитывается в байтах. Побеждает программа имеющая минимальный размер




Конкурс окончен

Первое место занимает @RusArt с ответом на 05AB1E, длиной всего 91 "байт".

Втрое место занимает @Anton Petrusevich с ответом на perl, длиной 150 байт.

И третье место достается @retorta с ответом на python, длиной 161 байт.

В ответах рассмотрены самые разные способы сжатия шрифта. При подготовке к конкурс
я рассматривал большинство из них. Самым простым для реализации и в то же время достаточн
эффективным оказалось сжатие до 70 байт в 7 битной, горизонтальной, кодировке (оригинально
моя кодировка представлена в ответе на postgresql. Даже удалось попасть в диапазон допустимы
символов, поменяв 6-7 биты и вычтя 2. Правда не все участники заморачивались и тратил
драгоценные байты на кодирование в диапазон печатных символов с 0x20 до 0x7E. Применяли кодирование как есть, часто с 8 битом или залезая вообще в диапазон управляющих. В принципе такое кодирование имеет право на жизнь, программы на той платформе, где писались работают. Хотя мне и не очень нравится когда программу нельзя напечатать на принтере, ввести по новой с листа и что бы при этом она продолжила работать (Вы не сможете опубликовать свой код в книге ;) ).

Победителем была использована совершенно другая кодировка шрифта. Словарь из 16 возможны
7и символьных элементов шрифта (в битовом кодировании) и кодовая таблица с номерам
частей для каждого символа, которая благодаря нестандартной кодовой странице языка 05AB1
заняла 35 "байт" (технически в этом языке используются 256 графем, которые принято считат
"байтами" потому что их именно 256 и если бы реально существовала такая кодовая таблица, то их все действительно можно было бы закодировать одним байтом). К сожалению подобное кодирование в других языках невозможно в принципе, в связи с тем, что из 256 значений байта в кодировке ASCII 31 используется для управляющих кодов, а 128 старших значений плохо переносятся между платформами.

Подобный подход со словарем в принципе был использован еще в нескольких ответах
но там не применялось бинарное кодирование в результате чего шрифт занимает горазд
больше места. В процессе подготовки конкурса я рассматривал даже несколько варианто
кодирования со словарем. Например, две комбинации ##### и #     #, встречающиеся очен
часто, предполагалось кодировать двумя битами со значениями 10 и 11, а остальные варианты 5и битным значением, первый бит которого 0, что бы отличить от первых двух и остальные 4 бита номер варианта. На практике же это давало совсем небольшой выигрыш в кодировании шрифта и при этом код декодирования оказывался слишком сложным и в код-гольфе неприменимым и опять же в обычном ascii коде уложить это очень сложно.

В ответах можно найти и совершенно иные способы кодирования, например, кодирование повторов # и пробелов просто количествами подряд, представленном в ответе @Qwertiy

К сожалению никто из участников не пробовал использовать вертикальное кодирование
т.е. где элементом выступает 7 бит кодируемого символа берущихся из него по вертикали
А при таком способе кодирования получается очень много повторов. Присмотритесь к цифра
5689 у них отличаются только 1 и последняя вертикаль, остальные одинаковы. При кодировании повторов в такой строке удается достичь практически такого же сжатия как и в случае с словарем и битовым кодированием. Но на практике гольфа опять же слабо применимое из за роста объема декодирующего кода. 



Альтернативный рейтинг

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


Anton Petrusevich, ответ на perl, длиной 150 байт
Андрей, ответ на C#, длиной 176 байт
Visman, ответ на PHP, длиной 235 байт




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



execute("ru.stackoverflow.com", "674415");
.cssload-container,.cssload-cube{width:97px;height:97px;transform-style:preserve-3d}.cssload-container,.cssload-cube,.cssload-half1,.cssload-half2{transform-style:preserve-3d}.cssload-container{position:relative;margin:23p
84px;perspective:292px}.cssload-cube{animation:cube 11.5s forwards infinite;transform-origin:cente
49px}.cssload-half1,.cssload-s1{top:0;transform-origin:50% 100%}.cssload-half1{height:39px;position:absolute;animation:half-fol
11.5s forwards infinite}.cssload-side{width:19px;height:19px;background:#ddd;position:absolute}.cssload-s1{left:39px;animation:s1an
11.5s forwards infinite}.cssload-s2,.cssload-s3,.cssload-s4{left:39px;transform-origin:50
0}.cssload-s2{top:19px;animation:s2ani 11.5s forwards infinite}.cssload-s3{top:39px;animation:s3an
11.5s forwards infinite}.cssload-s4{top:58px;animation:s4ani 11.5s forwards infinite}.cssload-s5{left:19px;top:19px;transform-origin:100
50%;animation:s5ani 11.5s forwards infinite}.cssload-s6{left:58px;top:39px;transform-origin:
50%;animation:s6ani 11.5s forwards infinite}@keyframes cube{0%,30%{transform:rotateX(0)}40%{transform:rotateX(45deg
rotateY(0) rotate(45deg)}60%{transform:rotateX(60deg) rotateY(0) rotate(45deg)}65%,70%{transform:rotateX(60deg
rotate(45deg) rotate(180deg)}75%,80%{transform:rotateX(60deg) rotate(45deg) rotate(1turn)}90%{transform:rotateX(0
rotate(0) rotate(0)}}@keyframes s1ani{0%{opacity:1;transform:translateY(0);background:#ddd}40%{transform:rotateX(0);background:#ddd}50%{transform:rotateX(-90deg);background:#ddd}90%{transform:rotateX(-90deg)}}@keyframes s2ani{0%{opacity:0;transform:rotateX(-179deg)}10%{opacity:1;transform:rotateX(0)}40%{background:#ddd}45%,80%{background:#b4b4b4}65%{opacity:1;background:#b4b4b4}90%{opacity:1}to{opacity:0}}@keyframes s3ani{0%,10%{opacity:0;transform:rotateX(-179deg)}20%,90%{opacity:1;transform:rotateX(0)}40%{background:#ddd}45%{background:#969696}to{opacity:0}}@keyframes s4ani{0%,20%{opacity:0;transform:rotateX(-179deg)}10%,to{opacity:0}30%{opacity:1;transform:rotateX(0)}40%{transform:rotateX(0);background:#ddd}50%{transform:rotateX(90deg);background:#b4b4b4}80%{background:#b4b4b4}90%{opacity:1;transform:rotateX(90deg)}}@keyframes s5ani{0%,10%{opacity:0;transform:rotateY(-179deg)}20%{opacity:1;background:#ddd;transform:rotateY(0)}40%{transform:rotateY(0)}50%{transform:rotateY(90deg)}55%{background:#ddd}60%{background:#c8c8c8}90%{transform:rotateY(90deg);opacity:1}to{opacity:0}}@keyframes s6ani{0%,20%{opacity:0;transform:rotateY(179deg)}30%{opacity:1;transform:rotateY(0)}40%{transform:rotateY(0)}50%{transform:rotateY(-90deg);background:#ddd}60%,80%{background:#c8c8c8}90%{opacity:1;transform:rotateY(-90deg)}to{opacity:0}}@keyframes half-fold{0%,50%{transform:rotateX(0)}60%,90%{transform:rotateX(-90deg)}}


Ответы

Ответ 1



05AB1E, -78 91 86 bytes 7F"‰ʒØTs“c7н7ƒÔΛ`"255öb7ô"KƶΘå驨ÈÐjη½ñ4BØ„ZƤ™Ωë₄´Ø#Ã.Dι¯ƒ“T"255öhIS7*N+èHè)»1'#‡0' ‡ Попробовать Словарь в 255-й системе счисления: ‰ʒØTs“c7н7ƒÔΛ` При преобразовании в двоичную и разделении на группы по 7 получаем это: ['1000001', '0011100', '0100010', '0001000', '0011000', '0101000', '0111110', '0000001', '1000000', '1111111', '1000010', '0000010', '1111110', '0000100', '0010000', '0111111'] Сам шрифт кодируется в 16-й системе счисления (в словаре 16 элементов) 1200021 3453336 6076889 6076709 8AAA9BB 988C706 608C006 9AD3EEE 6006006 600F706 В программе он также переведен в 255-ю систему счисления: KƶΘå驨ÈÐjη½ñ4BØ„ZƤ™Ωë₄´Ø#Ã.Dι¯ƒ“T Прогоняется цикл от 0 до 6, в каждой итерации ввод пользователя преобразуется в индекс шрифта, которые в свою очередь восстанавливаются по словарю. В конце программа все собирает и заменяет 0 на пробел и 1 на #. UPD Заменил использование • на явное преобразование системы счисления. ö = pop a,b push int(a, b) UPD Исправил цифру 3, заодно самоликвидировался костыль и код получился меньше Расшифровка 7 - кладем в стек число 7 F - берем из стека число a, в интервале от 0 до a выполняем следующие команды "‰ʒØTs“c7н7ƒÔΛ`" - кладем в стек строку 255 - кладем в стек число 255 ö - берем из стека a и b, выполняем преобразование системы счисления числа a с основания b к основанию 10, кладем результат в стек b - берем из стека число, преобразуем число в двоичную систему счисления, кладем результат в стек 7 - кладем в стек число 7 ô - берем из стека a и b, делим a на группы по b элементов, кладем в стек массив "KƶΘå驨ÈÐjη½ñ4BØ„ZƤ™Ωë₄´Ø#Ã.Dι¯ƒ“T" - кладем в стек строку 255 - кладем в стек число 255 ö - берем из стека a и b, выполняем преобразование системы счисления числа a с основания b к основанию 10, кладем результат в стек h - берем из стека число, переводим его в 16-ю систему счисления I - кладем в стек пользовательский ввод S - берем из стека строку и кладем массив ее символов 7 - кладем в стек число 7 * - берем из стека a и b, умножаем, кладем в стек результат. Тут массив умножается на число, то есть каждый элемент массива умножается на число N - кладем в стек текущий итератор из команды F (2-я по счету, N = [0..6]) + - берем из стека a и b, складываем, кладем в стек результат è - берем из стека a и b (a - шрифт, b - индекс), кладем в стек a[b] (код из словаря) H - переводим число из 16-й системы в 10-ю è - берем из стека a и b (a - словарь, b - индекс), кладем в стек a[b] (часть рисунка) ) - тут играет роль end F » - на этот момент в стеке есть строки с частями рисунка, команда соединяет их в одну и вставляет между ними перенос на новую строку 1 - кладем в стек число 1 '# - кладем в стек символ # ‡ - берем из стека a,b,c (a - рисунок, b - 1, c - #), в a заменяем b на с 0 - кладем в стек число 0 ' - кладем в стек пробел ‡ - берем из стека a,b,c (a - рисунок, b - 0, c - пробел), в a заменяем b на с в конце программа сама выводит на экран последний элемент стека (наш рисунок)

Ответ 2



Python3, 161 байт def f(s): for i in'0123456':print(*(format(b'>>@>>>"AAB@ABAAA(B@@AAA>>B~~>?A@AA"@AAAAA>>>>>>'[int(i+j)],'7b').translate({48:' ',49:'#'})for j in s)) SO режет некоторые символы, так что код скопированный отсюда не будет работать, но если скопировать отсюда, то всё работает. Проверка

Ответ 3



Perl, 230 165 155 153 150 символов и байт. Вся программа в ASCII, поэтому, число байт, занимамых программой, равно числу символов. Символы шрифта кодированы простой формулой, чтобы оставались печатными. $_="0987654321"; for$l(0..6){say map{sprintf("%08b",127&(80+ord substr'L8nnp/n/nnRHqqrpqrqqqX11rpp4qqq8nnr..8noq8p1/1q@q1R8pq2qq@qqLn/n2nn@nn',$l.$_))=~y/01/ #/r}/./g} Ideone: https://ideone.com/gbBuzn Комментарий: код эволюционировал, приведён последний вариант. С подсказками Mike Можно ужать ещё, наверное, пару символов, если формулу взять подсказанную, но тогда совсем моего авторства не останется :) Update: минус ещё два символа за счёт того, что ord берёт код первого символа строки. Спасибо Alexander Onokhov. Update: минус ещё три символа за счёт переупорядочивания данных шрифта и замены умножения на слияние строк. Идея "Someone Unknown".

Ответ 4



Javascript ES6, 376 286 символов Входные данные в переменной s. `235145251167151715251 1131323152524111615241115251 151111191624111615051215251 15131452511411616414526 15131316068625121415161 01131413161515111525121415251 0233517156125253155251`.replace(/./g,(m,i)=>' #'[i&1].repeat(m)).split` `.map(x=>s.replace(/./g,i=>x.substr(7*i,7)+" ")).join` ` Проверка: f = s => `235145251167151715251 1131323152524111615241115251 151111191624111615051215251 15131452511411616414526 15131316068625121415161 01131413161515111525121415251 0233517156125253155251`.replace(/./g,(m,i)=>' #'[i&1].repeat(m)).split` `.map(x=>s.replace(/./g,i=>x.substr(7*i,7)+" ")).join` ` document.querySelector("input").addEventListener("input", function (e) { document.querySelector("pre").textContent = f(e.target.value.replace(/\D/g, '')); }) body { display:inline-block; } input { position:sticky; left:8px; width:calc(100vw - 16px); box-sizing:border-box; }





Старая версия (376 символов)

Внимнание! Первая строка кода заканчивается пробелом!!!



f = s => ` 2#3 5# 4#5 2#5 # 6#7 #5 #7 #5 2#5 
 # 3# 3#2 3# 5#2 5#2 4# # 6# 5#2 4# # 5#2 5#
# 5# # # 9# 6#2 4# # 6#  9# 2# 5#2 5#
# 5# 3# 4#5 2#5 # 4# #6 #6 4# 4#5 2#6
# 5# 3# 3# 6 6#8 6#2 5# 2# 4# 5# 6#
 # 3# 4# 3# 6# 5# 5# # 5#2 5# 2# 4# 5#2 5#
 2#3 3#5 #7 #5 6# 2#5 2#5 3# 5#5 2#5 `.replace(/.\d/g,m=>m[0].repeat(m[1])).split`
`.map(x=>s.replace(/./g,i=>x.substr(7*i,7)+" ")).join`
`

document.querySelector("input").addEventListener("input", function (e) {
  document.querySelector("pre").textContent = f(e.target.value.replace(/\D/g, ''));
})
body { display:inline-block; }
input { position:sticky; left:8px; width:calc(100vw - 16px); box-sizing:border-box; }




Ответ 5



Javascript, 376 символов function printn(n) { var r=Array(7),s,i,d,k=0;while(n[k]){d='ab222ba7c777711231445123132148885dd546332112462215ef799912212211220321'.substr(n.split('')[k]*7 7).split('');for(i=0;i<7;i++){s=' ######, ##### ,# #, #,# ,#######,###### , # ,# # , # , ### , # # , ## , # ,# # , # '.split(',')[parseInt(d[i],16)];r[i]=r[i]?r[i]+' '+s:s}k++}return r.join('\n'); } // Test document.querySelector("input").addEventListener("input", function (e) { document.querySelector("pre").textContent = printn(e.target.value.replace(/\D/g, '')); }); document.querySelector("pre").textContent = printn('0123456789'); body { display:inline-block; } input { position:sticky; left:8px; width:calc(100vw - 16px); box-sizing:border-box; }



Ответ 6



PHP7.0, 211 189 182 символов @Mike уточнил в комментарии, что base64_decode() под запретом. Это тот же вариан на 252 символа, но строка со шрифтом представлена символами ASCII с кодами от 0 до 254, а функция base64_decode() удалена. Код не может быть нормально отображен в кодировке UTF-8

Ответ 7



PHP, 214 символов и байт function d($s){ $c='>>@>>>"AAB@ABAAA(B@@AAA>>B~~>?A@AA"@AAAAA>>>>>>'; for($l=0;$l<7;$l++){ for($d=0;$d

Ответ 8



Delphi, 407 символов Запишу пока что, потом подумаю еще. На C/C++, конечно, короче будет, хотя и над этим еще надо подумать, как ужать. program numbers; {$APPTYPE CONSOLE} uses SysUtils, strUtils; const a: array[0..6] of string = ( '1C083E3E407F3E7F3E3E', '22184141424040424141', '41280101424040044141', '41083E3E427E7E083E3F', '410840017F0141104101', '22084041024141104141', '1C3E7F3E023E3E103E3E'); const s = '1234567890'; var r,b: byte; c: char; x : word; begin for r:=0 to 6 do begin for c in s do begin x := strToInt('$'+copy(a[r],1+StrToInt(c)*2,2)); for b := 6 downto 0 do write(ifthen(x and (1 shl b)>=1, '#', ' ')); write(' '); end; writeln; end; end. зы: как длину то считаете

Ответ 9



Crystal 0.22, 250 байт def foo(s) 7.times{|i|s.each_byte{|b|8.times{|j|print (1&[0x1C22414141221C,0x3E0808080A0C08,0x7F01013E40413E,0x3E41403E40413E,0x20207F21212101,0x3E41403F01017F,0x3E41413F01413E,0x404040810217F,0x3E41413E41413E,0x3E41407E41413E][b-48]>>8*i+j)>0?"#": " "}};puts} end foo("0123456789"); https://play.crystal-lang.org/#/r/24o0 Основано на коде https://ru.stackoverflow.com/a/674586/253020

Ответ 10



C#, 313 305 304 302 299 281 279 278 277 276 275 261 259 258 252 250 байтов void f(string s){ for(int i=-1,j;++i<7;WriteLine())foreach(var c in s)for(Write(" "),j=0;j<7;)Write((new[]{-0x88F7EF9FCFA2,-0x1CB86CD9AB6,1056705L<<28,0,-0x790413A7D03D,0x102041,16449<<14,-0xE9EBC6CC0FBF,16385<<14,8193<<14}[c-48]+0xfa0c07d020be>>i*7+j++&1)>0?"#":" "); } ЗЫ. using static System.Console; - считаем как подключение стандартной библиотеки В немного более читабельном виде: using static System.Console; class Program { static void Main() { ShowNumber("0123"); ReadLine(); } static void ShowNumber(string s) { for (int i = -1, j; ++i < 7; WriteLine()) foreach (var c in s) for (Write(" "), j = 0; j < 7;) Write((new[] { -0x88F7EF9FCFA2, -0x1CB86CD9AB6, 1056705L<<28, 0, -0x790413A7D03D, 0x102041, 16449<<14, -0xE9EBC6CC0FBF, 16385<<14, 8193<<14 }[c - 48] + 0xfa0c07d020be >> i * 7 + j++ & 1) > 0 ? "#" : " "); } } Перенос объявления массива внутрь Write позволил выиграть 7 байт Перевод магических чисел в шестнадцатеричную систему позволил выиграть 8 байт Перевод '0' в 48 позволил выиграть еще один байт Чтение этого позволило избавиться от пары скобок и выиграть 2 байта Перенос вывода разделителей между цифрами в Write позволил уменьшить магические числа и выиграть 3 байта Убрал из подсчета заголовок функции Перенос WriteLine() внутрь заголовка for() позволил выкинуть пару скобок {} и выиграть 2 байта (подсмотрено в этом ответе) Замена операции сравнения == на < дает еще байт Вынос объявления переменных перед циклами позволил выиграть еще байт Перенос инкремента j++ в тело цикла позволил выиграть еще байт Перенос инкремента ++i в предусловие позволил выиграть еще байт Вынос постоянного слагаемого позволяет выиграть невероятные 14 байт (возможно выбор другого слагаемого может улучшить результат еще на 1-2 байта, но лень выбирать) Вынос вывода разделителя между цифрами в заголовок цикла позволил выиграть еще 2 байта Рекомендация @Qwertiy даёт еще один байт Формирование магических чисел с помощью сдвигов дает еще 6 байт Удалил лишнюю пару скобок (не понятно откуда она взялась) - 2 байта

Ответ 11



C++, 283 uint64_t a[]={0x1C22414141221C,0x3E0808080A0C08,0x7F01013E40413E,0x3E41403E40413E,0x20207F21212101,0x3E41403F01017F,0x3E41413F01413E,0x404040810217F,0x3E41413E41413E,0x3E41407E41413E};for(int i=0;i<7;++i,printf("\n"))for(int c:s)for(int I=0;I<8;++I)putchar(1&a[c-48]>>8*i+I?'#':' '); Читаемый код void foo(std::string s) { uint64_t a[]={0x1C22414141221C,0x3E0808080A0C08,0x7F01013E40413E,0x3E41403E40413E,0x20207F21212101,0x3E41403F01017F,0x3E41413F01413E,0x404040810217F,0x3E41413E41413E,0x3E41407E41413E}; for(int i=0;i<7;++i,printf("\n")) for(int c:s) for(int I=0;I<8;++I) putchar(1&a[c-48]>>8*i+I?'#':' '); } int main() { foo("0123456789"); return 0; } Проверка: https://ideone.com/gKYibz

Ответ 12



Java, 596 символов public class Main { public static void main(String[] args) { String s = "0123456789"; String i[] = s.split(""); int n[][] = { { 9, 8, 3, 3, 3, 8, 9 }, { 12, 10, 7, 12, 12, 12, 6 }, { 6, 3, 15, 6, 4, 4, 0 }, { 6, 3, 15, 6, 15, 3, 6 }, { 4, 2, 2, 2, 0, 14, 14 }, { 0, 4, 4, 1, 15, 3, 6 }, { 6, 3, 4, 1, 3, 3, 6 }, { 0, 2, 13, 12, 11, 11, 11 }, { 6, 3, 3, 6, 3, 3, 6 }, { 6, 3, 3, 5, 15, 3, 6 } }; String[] b = { "#######", "###### ", "# # ", "# #", "# ", " ######", " ##### ", " # # ", " # # ", " ### ", " ## ", " # ", " # ", " # ", " # ", " #" }; for (int m = 0; m < 7; m++) { for (int k = 0; k < i.length; k++) { System.out.print(b[n[Integer.parseInt(i[k])][m]] + " "); } System.out.print("\n"); } } } Краткий вариант: public class Main {public static void main(String[] args){String s="0123456789";Strin i[]=s.split("");int n[][]={{9,8,3,3,3,8,9},{12,10,7,12,12,12,6},{6,3,15,6,4,4,0},{6,3,15,6,15,3,6},{4,2,2,2,0,14,14},{0,4,4,1,15,3,6},{6,3,4,1,3,3,6},{0,2,13,12,11,11,11},{6,3,3,6,3,3,6},{6,3,3,5,15,3,6}};String[] b={"#######","###### ","# # ","# #","# "," ######"," ##### "," # # "," # # "," ### "," ## "," # "," # "," # "," # "," #"};for(int m=0; m<7;m++){for(int k=0;k

Ответ 13



PHP7.0, 235 символов Вариант со словарем как в этом ответе

Ответ 14



JavaScript, 726 cимволов, 757Кб) function p(s){ var d=[[7,' ###',' # #','# #',2,2,1,0],[5,' #',' ##','# #',' #',3,3,'f'],[7, #####','# #',' #',0,'#',4,'f'],[7,' #####','# #',' #',0,2,1,0],[7,'#',' #',1,1,'f',' #',5],[7,'f','#',1,'######',' #','# #',' ##### '],[7, #####','# #','#','######',1,1,0],[7,'f','# #',' #',' #',' #',4,4],[7, #####','# #',1,0,1,1,0],[7,' #####','# #',1,' ######',' #',1,0]];for(var h=0;h<7;h++){var ps='';for(var c of s){var cn=parseInt(c);var cd=d[cn];var cm=cd[h+1];var dl=cd[0];if(typeof cm=='number'){cm=d[cn][cm+1]}if(cm.indexOf('f')+1){cm='';for(var j=0;j

Ответ 15



Perl, -132 байта (вне конкурса, использует функцию кодирования) укороченный вариант решения от Anton Petrusevich $_="0987654321"; for$l(0..6){say map{(unpack'(B8)*','>>@>>>"AAB@ABAAA(B@@AAA>>B~~>?A@AA"@AAAAA>>>>>>')[$l.$_]=~y/01/ #/r}/./g} Использованы непечатные символы ASCII, поэтому копипаст отсюда работать не будет. На ideone можно увидеть рабочий пример. Пятая строка длиной 132 байта.

Ответ 16



Javascript ES6, 382 309 276 271 259 258 символов Входные данные в переменной s. eval("[[56b=16c=124cd=128g=254cgcc],[68 48e=130ef=132defee],[e80a=2afdd8ee],[ebccfh=252hbc126],[ebdagae32ea],[68bde4ee32ee],[56cgc4cc32cc]]"[R='replace'](/\d+|\w(?!=)/g,'$&,')).map(x=>s[R](/./g,m=>(256+x[m]).toString(2)[R](/./g,b=>" #"[b]).slice(1))).join` ` Проверка t = s => eval("[[56b=16c=124cd=128g=254cgcc],[68 48e=130ef=132defee],[e80a=2afdd8ee],[ebccfh=252hbc126],[ebdagae32ea],[68bde4ee32ee],[56cgc4cc32cc]]"[R='replace'](/\d+|\w(?!=)/g,'$&,')).map(x=>s[R](/./g,m=>(256+x[m]).toString(2)[R](/./g,b=>" #"[b]).slice(1))).join` ` document.querySelector("input").addEventListener("input", function (e) { document.querySelector("pre").textContent = t(e.target.value.replace(/\D/g, '')); }) body { display:inline-block; } input { position:sticky; left:8px; width:calc(100vw - 16px); box-sizing:border-box; }



Ответ 17



C#, 248 194 193 188 187 177 176 байтов Другой подход с упаковкой цифр не в long-константы, а в строку с символами, т. е., по сути, в число в 128-ричной системе: static void f(string s) { for(int i=-5,j;++i<3;WriteLine())foreach(var c in s)for(j=0;j<8;)Write(" #"[("~r``k!`!``Ln++Kk+K+++t**Kkkz+++r``Kaar` +rk*!*+f+*Lrk+J++f++~`!`J``f``"[i*10+c-8]-6^100)>>j++&1]); } Использует все наработки из моего предыдущего ответа + задействована хотелка из комментария. В более читабельной форме: using static System.Console; class Program { static void Main() { ShowNumber2("012345678"); ReadLine(); } static void ShowNumber2(string s) { for (int i = -5, j; ++i < 3; WriteLine()) foreach (var c in s) for (j = 0; j < 8;) Write(" #"[ ( "~r``k!`!``Ln++Kk+K+++t**Kkkz+++r``Kaar` +rk*!*+f+*Lrk+J++f++~`!`J``f``" [i * 10 + c - 8] - 6 ^ 100 ) >> j++ & 1 ]); } Проверка: http://ideone.com/xhiOgv Невероятно, но факт - один маленький XOR позволяет выкинуть из строки кучу управляющих последовательностей и выиграть ~50 байт! Рекомендация @Qwertiy даёт еще один байт Замена x^0b111...111 на ~x приносит 5 байтов и третье место в рейтинге Рекомендация @Mike дает еще один байт Благодаря помощи @Mike по подбору значения для XOR удалось выиграть еще 10 байт Еще одна хитрость приносит байт

Ответ 18



Ruby and Crystal -161b Вне конкурса вариант Антона (+Майка), просто чтобы показать синтаксис Ruby и Crystal, тут у них совпало. v="0987654321" 7.times{|l|puts v.chars.map{|c|("%08b"% (127&(80+"LRqqqRL8HX888nnq1npp/nq1n1qnprrr/22/pp.1qnnqp.qqn/r48@@@nqqnqqnnqqo1qn"[c.to_i*7+l].ord))).tr("01"," #")}.join} http://ideone.com/XvoUPZ

Ответ 19



PostrgeSQL (8.4+), 284 байта select string_agg(translate((ascii(substr(s,c::int+l::int*10+1,1))-2#96)::bit(8)::text,'01',' #'),' ') from (values('~j``"!`!``Dz##$"#$###Jcc$""f###j``$ j`a#j"c!c#r#cDj"#d##r##~`!`d``r``')) B(s), regexp_split_to_table('0123456','') l, regexp_split_to_table('9876543210','') c group by l order by l Число которое надо вывести задается в 4й строке. В примере на sqlfiddle.com пришлос заменить пробелы на подчеркивания, потому что там вывод на экран происходит в HTML (соседние пробелы подавляются), но разглядеть можно. На экране в pgAdmin (утилита из комплекта поставки postrgesql) выглядит замечательно.

Ответ 20



F#, 496 байт let p (s:string)= let f o v = String([|for i=(56-(o*8)) downto (56-(o*8)-7) do yield if (if i>32 then((v>>>30)&&&(1L<<<(i-31)))<>0L else(v&&&(1L <<< i-1))<>0L)then '#' else ' '|]) printfn "%s"(List.fold(fun a e-> a+(Array.fold(fun b v->b+(f e ([0x38448282824438L;0x1030501010107CL;0x7C82027C8080FEL;0x7C82027C02827CL;0x80848484FE0404L;0xFE8080FC02827CL;0x7C8280FC82827CL;0xFE840810202020L;0x7C82827C82827CL;0x7C82827E02827CL].[(int v - int '0')])))""(s.ToCharArray()))+"\n")"" [0..6]) Тест: https://ideone.com/i0hTW0

Ответ 21



PHP, 675 символов $a=[' ### # ##### ##### # ####### ##### ####### ##### #### ',' # # ## # # # # # # # # # # # # # # ','# # # # # # # # # # # # # # # ',' # # ##### ##### # # ###### ###### # ##### ###### ','# # # # # ####### # # # # # # # ',' # # # # # # # # # # # # # # # # ',' ### ##### ####### ##### # ##### ##### # ##### ##### '];for($l=0;$l<7;++$l){for($i=0;$i

Ответ 22



Второй вариант на PHP, с друго кодировкой алфавита PHP, 273 символа читабельный вариант: $s = "1234567890"; $n = [1,2,4,8,16,24,28,34,40,62,63,64,65,66,126,127]; $a = ['6399BF9F99','75CCDBBDCC','C800DBB2CC','C399DE9399','C3B0F0C4C0','73BC1CC4CC','69F9199499']; foreach($a as $l){ for($i=0;$i=0;$b--){ echo $n[hexdec($l[$s[$i]])]&1<<$b?'#':' '; } ?> =0;$b--)echo $n[hexdec($l[$s[$i]])]&1<<$b?'#':' ';?>

Ответ 23



PHP, 465 символов $a = "###"; $z = " "; $s = "0123456789"; $i = str_split($s); $n = [[9, 8, 3, 3, 3, 8, 9], [12, 10, 7, 12, 12, 12, 6], [6, 3, 15, 6, 4, 4, 0] [6, 3, 15, 6, 15, 3, 6], [4, 2, 2, 2, 0, 14, 14], [0, 4, 4, 1, 15, 3, 6], [6, 3, 4, 1, 3, 3, 6], [0, 2, 13, 12, 11, 11, 11], [6, 3, 3, 6, 3, 3, 6], [6, 3, 3, 5, 15, 3, 6]]; $b = ["#$a$a", "$a$a ", "# $z# ", "# $z#", "#$z$z", " $a$a", " ##$a ", " # #$z", " #$z# ", " $a ", " ##$z", " # $z", "$z#$z", " $z# ", " $z# ", "$z$z#"]; for ($m = 0; $m < 7; $m++) { for ($k = 0; $k < count($i); $k++) { echo $b[$n[$i[$k]][$m]] . " "; } echo "\n"; } Краткий вариант $a="###";$z=" ";$s="0123456789";$i=str_split($s);$n=[[9,8,3,3,3,8,9],[12,10,7,12,12,12,6],[6,3,15,6,4,4,0],[6,3,15,6,15,3,6],[4,2,2,2,0,14,14],[0,4,4,1,15,3,6],[6,3,4,1,3,3,6],[0,2,13,12,11,11,11],[6,3,3,6,3,3,6],[6,3,3,5,15,3,6]];$b=["#$a$a","$a$a ","# $z# ","# $z#","#$z$z"," $a$a"," ##$a "," # #$z"," #$z# "," $a "," ##$z", " # $z","$z#$z"," $z# "," $z# ","$z$z#"];for($m=0;$m<7;$m++){for($k=0;$k

Ответ 24



Аналог кода на Delphi из предыдущего ответа переписанный на php. PHP, 285 символов читабельный вариант: $s = "1234567890"; $a=['1C083E3E407F3E7F3E3E','22184141424040424141','41280101424040044141','41083E3E427E7E083E3F','410840017F0141104101','22084041024141104141','1C3E7F3E023E3E103E3E']; foreach($a as $l){ for($i=0; $i=0; $b--) echo hexdec(substr($l,$i*2,2))&1<<$b?'#':' '; ?> =0;$b--)echo hexdec(substr($l,$i*2,2))&1<<$b?'#':' ';?>

Ответ 25



Python 3, 244 байта (без учёта первых двух строк) s=input() from re import sub for r in range(7):print(*(eval(sub('(.)'*2,r'+" "*0x\1+"#"*\2',hex(int('3BKFT57KVYH9NKP5RWAHYETF85SH2KMQ1BKLL2CVX0HPWX1W2WBGPNN1SHAZPXK7JS0JP0V493SO5PSD1JDSIOB1VMCY67RQ2N7X94FSBVDX9MV0UCUVVSK5GR0DHCG',36)))[15:])[int(d)*7+r*70:][:7]for d in s))

Ответ 26



C#, 252 байта Весь код: http://ideone.com/SzOJCG Только функция: int i=0,j;for(;i<70;i+=10){foreach(var c in s)for(j=0;j<8;j++)Write(new BitArray(new[]{(int)"\x1\x4\x8\x10\x14\x18\x1 !8>?@AD|~\x7f\uc282"["JDKKARKRKKOFNNIANINNSEMMIAADNNSDKKILLCKQSDAMRMNBNMODANHNNBNNJPRKHKKBKK"[i+c-48]-65]})[j]?'#':' ');WriteLine();}

Ответ 27



Kotlin, 233 234 243 268, зато без непечатных символов :) (0..6).map{c->s.map{(BigInteger("cq7ntkqmgle8bf3logj0a8x7uwoe4u3x2dpfv5x5hvwz7z25fob8oanyd2cs7xt99qeaoj1bnj3jw1guzhbzknwgyi7cdz5",36).toString(2).replace('0','4').drop(c*70+it.toInt()*7-336).take(7)+"1").map{print(it-17)}};println()} Развернуто import java.math.BigInteger fun main(args: Array) { val s = "110123456789" (0..6).map{c-> s.map{(BigInteger("cq7ntkqmgle8bf3logj0a8x7uwoe4u3x2dpfv5x5hvwz7z25fob8oanyd2cs7xt99qeaoj1bnj3jw1guzhbzknwgyi7cdz5",36) .toString(2) .replace('0','4') .drop(c*70+it.toInt()*7-336) .take(7)+"1" ) .map{print(it-17)} } println() } } Попробовать можно тут, просто скопировать туда целиком развернутый код