Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 10976 сообщений
Cообщения с меткой

разработка - Самое интересное в блогах

Следующие 30  »
kiev2376393

Великобритания срочно взялась за разработку лазерного оружия

Вторник, 23 Августа 2016 г. 19:03 (ссылка)

Застряли в прошлом. Великобритания собирается потратить сотни миллионов на разработку военной техники следующего поколения. Что собираются создавать , знает корреспондент Светлана Чернецкая, она в Лондоне. Превзойти всех. Великобритания поставила перед

Читать далее...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Пишите меньше кода, блин

Вторник, 23 Августа 2016 г. 10:35 (ссылка)

Я не самый талантливый кодер в мире. Правда. Так что я стараюсь писать как можно меньше кода. Чем меньше кода я пишу, тем меньше кода может ломаться, поддерживаться и требовать пояснений.



А еще я ленивый — мед, да еще и ложкой (я решил использовать в статье аналогии с едой).



Но, оказывается, что единственный гарантированный способ повысить производительность в вебе — это писать меньше кода. Минифицировать? Окей. Сжимать? Ну, да. Кэшировать? Звучит неплохо. Вообще отказываться кодить или использовать чужой код изначально? А вот теперь — в яблочко! Что есть на входе — должно выйти на выходе в той или иной форме, независимо от того, смог ли ваш сборщик растворить и переварить это своими желудочными соками (я, пожалуй, откажусь от пищевых аналогий).



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



В стремлении к уменьшению мне больше всего нравится вот что: в конце остается только то, что реально нужно, только то, что по-настоящему требуется пользователю. Огромная фотка какого-то чувака, пьющего латте? Выкинуть. Кнопки социальных сетей, которые подсасывают кучу левого кода и ломают дизайн страницы? Пинок под зад им. Эта хреновина на JavaScript, которая перехватывает правый клик и показывает кастомное модальное окно? Выставить на мороз!



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



WAI-ARIA



Во-первых, WAI-ARIA != web accessibility. Это просто инструмент для улучшения совместимости с некоторыми вспомогательными технологиями вроде экранного диктора. Поэтому первое правило ARIA — это не использовать его, если не требуется.



LOL, нет:



Subheading


Да:



Subheading



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




My checkbox label


Стили? Не волнуйтесь, все под контролем. Если вообще нужны кастомные стили, конечно.






Сетка



Вы когда-нибудь получали удовольствие от использования или чтения веб-сайта с более чем двумя колонками? Слишком много информации, которая требует внимания. «Интересно, какая из этих штук, похожих на навигацию, на самом деле является нужной мне навигацией?». Это риторический вопрос: я не знаю, что делать дальше и покидаю сайт.



Конечно, иногда хочется поставить какую-нибудь штуку рядом с другой штукой. Например, поисковую выдачу или еще что-то. Но зачем тянуть сюда тонну библиотек для сеток? Флексбокс умеет это буквально с двумя блоками определений.



.grid {
display: flex;
flex-flow: row wrap;
}

.grid > * {
flex-basis: 10em;
flex-grow: 1;
}


Теперь все будет растягиваться до примерно 10em в ширину. Количество колонок зависит от того, сколько ячеек размером примерно 10em поместится в viewport. Готово. Идем дальше. А, кстати, давайте еще обсудим такую штуку:



width: 57.98363527356473782736464546373337373737%;


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



Отступы



Мы знаем, как это делается. Задавайте отступ для всех элементов, используя универсальные селекторы. И переопределяйте когда нужно. Много не потребуется.



body * + * {
margin-top: 1.5rem;
}


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



Вид



Не нужен целый Angular или Meteor чтобы поделить простую страницу на "views". Views это просто куски страницы, которые видны в те моменты, когда не видны другие. Это можно сделать с CSS:



.view {
display: none;
}

.view:target {
display: block;
}


«Но одностраничные приложения запускают код при загрузке view!», — скажете вы. Я понимаю. Для этого существует событие onhashchange. Не нужно библиотек, и ваши ссылки будут нормальными, стандартными, их можно добавлять в закладки. Это клево. Об этом можно почитать больше, если интересно.



Размер шрифта



Тонкая настройка размера шрифта может сильно раздуть ваши блоки media. Поэтому нужно отдать это в руки CSS. Одна строчка кода.



font-size: calc(1em + 1vw);


Э-э-э… вот и все. Есть даже минимальный размер, так что не будет нечитаемых крохотных букв на мобильных устройствах.



10k Apart



Как я сказал, я не лучший в мире кодер. Я просто знаю несколько трюков. Но с небольшим количеством знаний можно сделать очень многое. В этом суть соревнования «10k Apart» — выяснить, чего можно добиться с 10kb или меньше. Есть большие призы. Я как судья не могу дождаться, когда возьмусь за изучение всех крутых заявок, идей и реализаций, которые мне хотелось бы придумать самому. А что придумаете вы?


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/308308/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Массивы в РНР 7: хэш-таблицы

Понедельник, 22 Августа 2016 г. 19:17 (ссылка)

Хэш-таблицы используются везде, в каждой серьёзной С-программе. По сути, они позволяют программисту хранить значения в «массиве», индексируя его с помощью строк, в то время как в языке С допускаются только целочисленные ключи массива. В хэш-таблице строчные ключи сначала хэшируются, а затем уменьшаются до размеров таблицы. Здесь могут возникать коллизии, поэтому нужен алгоритм их разрешения. Существует несколько подобных алгоритмов, и в РНР используется стратегия связных списков (linked list).



В Сети есть немало замечательных статей, подробно освещающих устройство хэш-таблиц и их реализации. Начать можно с http://preshing.com/. Но имейте в виду, вариантов структуры хэш-таблиц — несметное множество, и ни один из них не совершенен, в каждом есть компромиссы, несмотря на оптимизацию циклов процессора, использования памяти или хорошее масштабирование потокового окружения (threaded environment). Одни варианты лучше при добавлении данных, другие — при поиске и т. д. Выбирайте реализацию в зависимости от того, что для вас важнее.



Хэш-таблицы в РНР 5 подробно рассмотрены в материале phpinteralsbook, который я написал вместе с Nikic, автором хорошей статьи про хэш-таблицы в РНР 7. Возможно, её вы тоже сочтёте интересной. Правда, она писалась до релиза, поэтому некоторые вещи в ней слегка отличаются.



Здесь же мы подробно рассмотрим, как устроены хэш-таблицы в РНР 7, как с ними можно работать с точки зрения языка С и как ими управлять средствами РНР (используя структуры, называемые массивами). Исходный код в основном доступен в zend_hash.c. Не забывайте, что хэш-таблицы мы используем везде (обычно в роли словарей), следовательно, нужно проектировать их так, чтобы они быстро обрабатывались процессором и потребляли мало памяти. Эти структуры решающе влияют на общую производительность РНР, поскольку местные массивы не единственное место, где используются хэш-таблицы.



Конструкция хэш-таблицы



Есть ряд положений, которые мы далее подробно рассмотрим:




  • Ключ может быть строкой или целочисленным. В первом случае используется структура zend_string, во втором — zend_ulong.

  • Хэш-таблица всегда должна помнить порядок добавления её элементов.

  • Размер хэш-таблицы меняется автоматически. В зависимости от обстоятельств она самостоятельно уменьшается или увеличивается.

  • С точки зрения внутренней реализации размер таблицы всегда равен двойке в степени. Это делается для улучшения производительности и выравнивания размещения данных в памяти.

  • Все значения в хэш-таблице хранятся в структуре zval, больше нигде. Zval’ы могут содержать данные любых типов.



Рассмотрим структуру HashTable:



struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar nIteratorsCount,
zend_uchar reserve)
} v;
uint32_t flags; /* доступно 32 флага */
} u;
uint32_t nTableMask; /* маска — nTableSize */
Bucket *arData; /* полезное хранилище данных */
uint32_t nNumUsed; /* следующая доступная ячейка в arData */
uint32_t nNumOfElements; /* общее количество занятых элементов в arData */
uint32_t nTableSize; /* размер таблицы, всегда равен двойке в степени */
uint32_t nInternalPointer; /* используется для итерации */
zend_long nNextFreeElement; /* следующий доступный целочисленный ключ */
dtor_func_t pDestructor; /* деструктор данных */
};


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



Размер этой структуры — 56 байт (согласно модели LP64).



Самое интересное поле данных — arData, это своеобразный указатель на область памяти цепочки Bucket. Сама Bucket представляет собой одну ячейку в массиве:



typedef struct _Bucket {
zval val; /* значение */
zend_ulong h; /* хэш (или числовой индекс) */
zend_string *key; /* строковый ключ или NULL для числовых значений */
} Bucket;


Как вы могли заметить, в структуре Bucket будет храниться zval. Обратите внимание, что здесь используется не указатель на zval, а именно сама структура. Так сделано потому, что в РНР 7 zval’ы больше не размещаются в куче (в отличие от PHP 5), но при этом в РНР 7 может размещаться целевое значение, хранящееся в zval в виде указателя (например, строка РНР).



Давайте посмотрим на картинке, как происходит размещение в памяти:







Как видите, помещаемые в хэш-таблицу данные хранятся в смежном разделе памяти: arData.



Добавление элементов с сохранением очерёдности



РНР должен сохранять очерёдность элементов при их добавлении в массив. Если применить к массиву инструкцию foreach, то вы получите данные именно в том порядке, в котором они помещались в массив, вне зависимости от их ключей:



$a = [9=>"foo", 2 => 42, []];
var_dump($a);

array(3) {
[9]=>
string(3) "foo"
[2]=>
int(42)
[10]=>
array(0) {
}
}


Это важный момент, наложивший ряд ограничений при реализации хэш-таблиц. Все данные размещаются в памяти рядом друг с другом. В zval’ах они хранятся упакованными в Bucket’ы, которые располагаются в полях С-массива arData. Примерно так:



$a = [3=> 'foo', 8 => 'bar', 'baz' => []];






Благодаря этому подходу можно легко итерировать хэш-таблицу: достаточно всего лишь итерировать массив arData. По сути, это быстрое последовательное сканирование памяти, потребляющее очень мало ресурсов процессорного кэша. В одной строке могут помещаться все данные из arData, а доступ к каждой ячейке занимает около 1 наносекунды. Обратите внимание: чтобы повысить эффективность использования процессорного кэша, arData выравнен в соответствии с 64-битной моделью (также используется оптимизирующее выравнивание 64-битных полных инструкций). Код итерирования хэш-таблицы может выглядеть так:



size_t i;
Bucket p;
zval val;

for (i=0; i < ht->nTableSize; i++) {
p = ht->arData[i];
val = p.val;
/* можно что-нибудь сделать с val */
}


Данные сортируются и передаются в следующую ячейку arData. Для выполнения этой процедуры достаточно просто помнить следующую доступную ячейку в этом массиве, хранящуюся в поле nNumUsed. При каждом добавлении нового значения мы помещаем его ht->nNumUsed++. Когда количество элементов в nNumUsed достигает количества элементов в хэш-таблице (nNumOfElements), мы запускаем алгоритм уплотнения или изменения размера (compact or resize), о котором поговорим ниже.



Упрощённое представление добавления элементов в хэш-таблицу с использованием строковых ключей:



idx = ht->nNumUsed++; /* берём номер следующей доступной ячейки */
ht->nNumOfElements++; /* инкрементируем количество элементов */
/* ... */
p = ht->arData + idx; /* получаем в этой ячейке bucket от arData */
p->key = key; /* устанавливаем ключ, перед которым собираемся вставить */
/* ... */
p->h = h = ZSTR_H(key); /* сохраняем в bucket хэш текущего ключа */
ZVAL_COPY_VALUE(&p->val, pData); /* копируем значение в значение bucket’а: операция добавления */


Стирание значений



При удалении значения массив arData не уменьшается и не перегруппируется. В противном случае производительность бы катастрофически упала, поскольку нам бы приходилось перемещать данные в памяти. Поэтому при удалении данных из хэш-таблицы соответствующая ячейка в arData просто помечается специальным образом: zval UNDEF.







Следовательно, итерационный код нужно немного перестроить, чтобы он мог обрабатывать такие «пустые» ячейки:



size_t i;
Bucket p;
zval val;

for (i=0; i < ht->nTableSize; i++) {
p = ht->arData[i];
val = p.val;
if (Z_TYPE(val) == IS_UNDEF) {
continue;
}
/* можно что-нибудь сделать с val */
}


Ниже мы рассмотрим, что происходит при изменении размера хэш-таблицы и как должен быть реорганизован массив arData, чтобы исчезли «пустые» ячейки (уплотнение).



Хэширование ключей



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



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



Например: если добавить сначала по ключу foo, а потом по ключу bar, то первый будет хэширован/сжат до ключа 5, а второй — до ключа 3. Если данные foo хранить в arData[5], а данные bar — в arData[3], то получится, что данные bar идут до данных foo. И при итерировании arData элементы будут передаваться уже не в том порядке, в котором они добавлялись.







Итак, мы хэшируем, а потом сжимаем ключ, чтобы он поместился в отведённые границы arData. Но мы не используем его как есть, в отличие от PHP 5. Необходимо сначала преобразовать ключ с помощью таблицы преобразования (translation table). Она просто сопоставляет одно целочисленное значение, полученное в результате хэширования/сжатия, и другое целочисленное значение, используемое для адресации в рамках массива arData.



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







Теперь наш ключ foo хэширован в DJB33X и сжат по модулю до необходимого размера (nTableMask). Полученное значение — это индекс, который можно использовать, чтобы обращаться к ячейкам преобразования arData (а не к прямым ячейкам!).



Доступ к этим ячейкам происходит с помощью отрицательного сдвига от начальной позиции arData. Две области памяти были объединены, поэтому мы можем хранить данные в памяти последовательно. nTableMask соответствует отрицательному значению размера таблицы, так что, взяв его в качестве модуля, получим значение от 0 до –7. Теперь можно обращаться к памяти. При размещении в ней всего буфера arData мы рассчитываем его размер по формуле:



Размер таблицы * размер bucket’а + размер таблицы * размер(uint32) ячеек преобразования.



Ниже хорошо видно, как буфер делится на две части:



#define HT_HASH_SIZE(nTableMask) (((size_t)(uint32_t)-(int32_t)(nTableMask)) * sizeof(uint32_t))
#define HT_DATA_SIZE(nTableSize) ((size_t)(nTableSize) * sizeof(Bucket))
#define HT_SIZE_EX(nTableSize, nTableMask) (HT_DATA_SIZE((nTableSize)) + HT_HASH_SIZE((nTableMask)))
#define HT_SIZE(ht) HT_SIZE_EX((ht)->nTableSize, (ht)->nTableMask)

Bucket *arData;
arData = emalloc(HT_SIZE(ht)); /* теперь разместим это в памяти */


Когда макросы выполнены, мы получаем:



(((size_t)(((ht)->nTableSize)) * sizeof(Bucket)) + (((size_t)(uint32_t)-(int32_t)(((ht)->nTableMask))) * sizeof(uint32_t)))


Замечательно.



Разрешение коллизий



Теперь разберёмся, как разрешаются коллизии. Как вы помните, в хэш-таблице несколько ключей при хэшировании и сжатии могут соответствовать одному и тому же индексу преобразования. Так что, получив индекс преобразования, мы с его помощью извлекаем данные обратно из arData и сравниваем хэши и ключи, проверяя, то ли это, что нужно. Если данные неправильные, мы проходим по связному списку с помощью поля zval.u2.next, в котором отражается следующая ячейка для внесения данных.



Обратите внимание, что связный список не рассеян по памяти, как традиционные связные списки. Вместо того чтобы ходить по нескольким размещённым в памяти указателям, полученным от кучи — и наверняка разбросанным по адресному пространству, — мы считываем из памяти полный вектор arData. И это одна из главных причин увеличения производительности хэш-таблиц в РНР 7, а также всего языка.



В РНР 7 у хэш-таблиц очень высокая локальность данных. В большинстве случаев доступ происходит за 1 наносекунду, поскольку данные обычно находятся в процессорном кэше первого уровня.



Давайте посмотрим, как можно добавлять элементы в хэш, а также разруливать коллизии:



idx = ht->nNumUsed++; /* берём номер следующей доступной ячейки */
ht->nNumOfElements++; /* инкрементируем количество элементов */
/* ... */
p = ht->arData + idx; /* получаем от arData bucket в этой ячейке */
p->key = key; /* помечаем ключ для вставки */
/* ... */
p->h = h = ZSTR_H(key); /* сохраняем в bucket хэш текущего ключа */
ZVAL_COPY_VALUE(&p->val, pData); /* копируем значение в значение bucket’а: операция добавления */

nIndex = h | ht->nTableMask; /* получаем индекс таблицы преобразования */
Z_NEXT(p->val) = HT_HASH(ht, nIndex); /* помести следующего из нас как фактический элемент */
HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); /* помести нас в фактическую ячейку преобразования */


То же самое и с удалением:



h = zend_string_hash_val(key); /* получаем хэш из строкового ключа */
nIndex = h | ht->nTableMask; /* получаем индекс таблицы преобразования */

idx = HT_HASH(ht, nIndex); /* получаем ячейку, соответствующую индексу преобразования */
while (idx != HT_INVALID_IDX) { /* если есть соответствующая ячейка */
p = HT_HASH_TO_BUCKET(ht, idx); /* получаем bucket из этой ячейки */
if ((p->key == key) || /* это правильный? Тот же указатель ключа? */
(p->h == h && /* ...или тот же хэш */
p->key && /* и ключ (на базе строкового ключа) */
ZSTR_LEN(p->key) == ZSTR_LEN(key) && /* и та же длина ключа */
memcmp(ZSTR_VAL(p->key), ZSTR_VAL(key), ZSTR_LEN(key)) == 0)) { /* и то же содержимое ключа? */
_zend_hash_del_el_ex(ht, idx, p, prev); /* вот они мы! Удаляй нас */
return SUCCESS;
}
prev = p;
idx = Z_NEXT(p->val); /* переместиться к следующей ячейке */
}
return FAILURE;


Ячейки преобразования и инициализация хэша



HT_INVALID_IDX — специальный флаг, который мы помещаем в таблицу преобразования. Он означает «это преобразование никуда не ведёт, продолжать не нужно».



Двухэтапная инициализация даёт определённые преимущества, позволяя максимально уменьшить влияние пустой, только что созданной хэш-таблицы (частый случай в РНР). При создании таблицы мы одновременно создаём ячейки bucket’ов в arData и две ячейки преобразования, в которые помещаем флаг HT_INVALID_IDX. Затем накладываем маску, которая направляет в первую ячейку преобразования (HT_INVALID_IDX, здесь нет данных).



#define HT_MIN_MASK ((uint32_t) -2)
#define HT_HASH_SIZE(nTableMask) (((size_t)(uint32_t)-(int32_t)(nTableMask)) * sizeof(uint32_t))
#define HT_SET_DATA_ADDR(ht, ptr) do { (ht)->arData = (Bucket*)(((char*)(ptr)) + HT_HASH_SIZE((ht)->nTableMask)); } while (0)

static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX};

/* hash lazy init */
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
{
/* ... */
ht->nTableSize = zend_hash_check_size(nSize);
ht->nTableMask = HT_MIN_MASK;
HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
}


Обратите внимание, что здесь можно не пользоваться кучей. Вполне достаточно статической постоянной области памяти (static const memory zone), так получается гораздо легче (uninitialized_bucket).



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



(ht)->nTableMask = -(ht)->nTableSize;
HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), (ht)->u.flags & HASH_FLAG_PERSISTENT));
memset(&HT_HASH(ht, (ht)->nTableMask), HT_INVALID_IDX, HT_HASH_SIZE((ht)->nTableMask))


Макрос HT_HASH позволяет обращаться к ячейкам преобразования в той части размещённого в памяти буфера, для которой использовано отрицательное смещение. Табличная маска всегда отрицательная, потому что ячейки таблицы преобразования индексируются в минус от начала буфера arData. Здесь во всей красе раскрывается программирование на С: вам доступны миллиарды ячеек, плавайте в этой бесконечности, только не утоните.



Пример лениво инициализированной (lazy-initialized) хэш-таблицы: она создана, но пока ни один хэш в неё не помещён.







Фрагментация хэшей, увеличение и уплотнение



Если хэш-таблица заполнена целиком и нужно добавить новые элементы, приходится увеличивать её размер. Это большое преимущество хэш-таблиц по сравнению с классическими жёстко ограниченными массивами в С. При каждом увеличении размер хэш-таблицы вырастает вдвое. Напоминаю, что при увеличении размера таблицы мы предварительно распределяем в памяти С-массив arBucket, а в пустые ячейки помещаем специальные значения UNDEF. В результате мы бодро теряем память. Потери вычисляются по формуле:



(новый размер – старый размер) * размер Bucket



Вся эта память состоит из UNDEF-ячеек и ждёт, когда в неё поместят данные.



К примеру, у вас в хэш-таблице 1024 ячейки, и вы добавляете новый элемент. Таблица разрастается до 2048 ячеек, из которых 1023 — пустые. 1023 * 32 байта = примерно 32 Кб. Это один из недостатков реализации хэш-таблиц в РНР.



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



Пример сильно фрагментированной 8-ячеечной хэш-таблицы:







Как вы помните, в UNDEF-ячейках нельзя хранить новые значения. В вышеприведённой схеме при итерировании хэш-таблицы мы идём от arData[0] к arData[7].



Увеличив размер, можно сократить вектор arData и в конце концов заполнить пустые ячейки, просто перераспределив данные. Когда таблице дают команду на изменение размера, то в первую очередь она пытается себя уплотнить. Затем вычисляет, придётся ли ей после уплотнения снова увеличиваться. И если оказывается, что да, то таблица увеличивается в два раза. После этого вектор arData начинает занимать вдвое больше памяти (realloc()). Если же увеличиваться не нужно, то данные просто перераспределяются в уже размещённых в памяти ячейках. Здесь используется алгоритм, который мы не можем применять при каждом удалении элементов, поскольку он слишком часто тратит ресурсы процессора, а выхлоп не так велик. Вы же помните о знаменитом программистском компромиссе между процессором и памятью?



На этой иллюстрации представлена предыдущая фрагментированная хэш-таблица после уплотнения:







Алгоритм просматривает arData и заполняет каждую UNDEF-ячейку данными из следующей неUNDEF-ячейки. Упрощённо это выглядит так:



Bucket *p;
uint32_t nIndex, i;
HT_HASH_RESET(ht);
i = 0;
p = ht->arData;

do {
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) {
uint32_t j = i;
Bucket *q = p;
while (++i < ht->nNumUsed) {
p++;
if (EXPECTED(Z_TYPE_INFO(p->val) != IS_UNDEF)) {
ZVAL_COPY_VALUE(&q->val, &p->val);
q->h = p->h;
nIndex = q->h | ht->nTableMask;
q->key = p->key;
Z_NEXT(q->val) = HT_HASH(ht, nIndex);
HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
if (UNEXPECTED(ht->nInternalPointer == i)) {
ht->nInternalPointer = j;
}
q++;
j++;
}
}
ht->nNumUsed = j;
break;
}
nIndex = p->h | ht->nTableMask;
Z_NEXT(p->val) = HT_HASH(ht, nIndex);
HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
p++;
} while (++i < ht->nNumUsed);


API хэш-таблицы



Ну что же, теперь мы знаем основные моменты реализации хэш-таблицы в PHP 7. Давайте теперь рассмотрим её публичный API.



Тут особо говорить не о чем (ну, в РНР 5 API сделан куда лучше). Просто при использовании функции API не забывайте о трёх вещах:




  • О вашей операции (добавление, удаление, очистка, уничтожение и т. д.).

  • О типе вашего ключа (целочисленный или строчный).

  • О типе данных, которые вы хотите хранить.





Каким бы ни был ваш ключ, строчным или целочисленным, главное: API должен знать о том, что строчные ключи получают хэши от zend_string, а целочисленные сразу используются в качестве хэша. Поэтому вы можете встретить zend_hash_add(ht, zend_string, data) или zend_hash_index_add(ht, long, data).



Иногда ключ может представлять собой простую пару (char* / int). В этом случае нужно использовать другой API, например zend_hash_str_add(ht, char *, int, data). Помните, что, как бы там ни было, хэш-таблица обратится к zend_string, превратив в него ваш строковый ключ и вычислив его хэш, потратив какое-то количество ресурсов процессора. Если можете использовать zend_string, используйте. Наверняка они уже вычислили свои хэши, поэтому API просто возьмёт их. Например, компилятор РНР вычисляет хэш каждой части используемого строкового, как zend_string. OPCache хранит подобный хэш в памяти общего доступа. Как автор расширений, рекомендую инициализировать в MINIT все ваши литералы zend_string.



Теперь о данных, которые вы собираетесь хранить в хэш-таблице. Опять же, это может быть что угодно, хэш-таблица всё равно поместит данные в zval, имеющийся в каждом Bucket. В РНР 7 zval’ы могут хранить любые данные любого типа. В целом API хэш-таблицы ожидает, что вы упакуете данные в zval, который API воспринимает как значение.



Ситуацию можно несколько упростить, если у вас есть указатель на хранилище или область памяти (данные, на которые ссылается указатель). Тогда API поместит в zval этот указатель или область памяти, а потом сам zval использует указатель в качестве данных.



Примеры помогут понять идею:



zend_hash_str_add_mem(hashtable *, char *, size_t, void *)
zend_hash_index_del(hashtable *, zend_ulong)
zend_hash_update_ptr(hashtable *, zend_string *, void *)
zend_hash_index_add_empty_element(hashtable *, zend_ulong)


При извлечении данных вы получите либо zval *, либо NULL. Если в качестве значения используется указатель, API может вернуть как есть:



zend_hash_index_find(hashtable *, zend_string *) : zval *
zend_hash_find_ptr(hashtable *, zend_string *) : void *
zend_hash_index_find(hashtable *, zend_ulong) : zval *


Что касается _new API наподобие zend_hash_add_new(), его лучше не использовать. Его применяет движок для внутренних нужд. Этот API заставляет хэш-таблицу хранить данные, даже если они уже доступны в хэше (тот же ключ). В результате у вас появятся дубли, что может не лучшим образом сказаться на работе. Так что использовать этот API можно, только если вы полностью уверены, что в хэше нет данных, которые вы собираетесь добавить. Тем самым вы избежите необходимости искать их.



Напоследок: как и в РНР 5, API zend_symtable_api() заботится о преобразовании текстового написания чисел непосредственно в числовое:



static zend_always_inline zval *zend_symtable_update(HashTable *ht, zend_string *key, zval *pData)
{
zend_ulong idx;

if (ZEND_HANDLE_NUMERIC(key, idx)) { /* handle numeric key */
return zend_hash_index_update(ht, idx, pData);
} else {
return zend_hash_update(ht, key, pData);
}
}


Вы можете использовать множество макросов для итераций в зависимости от того, какие данные хотите получить в цикле: ключ, zval… Все эти макросы основаны на ZEND_HASH_FOREACH:



#define ZEND_HASH_FOREACH(_ht, indirect) do { \
Bucket *_p = (_ht)->arData; \
Bucket *_end = _p + (_ht)->nNumUsed; \
for (; _p != _end; _p++) { \
zval *_z = &_p->val; \
if (indirect && Z_TYPE_P(_z) == IS_INDIRECT) { \
_z = Z_INDIRECT_P(_z); \
} \
if (UNEXPECTED(Z_TYPE_P(_z) == IS_UNDEF)) continue;

#define ZEND_HASH_FOREACH_END() \
} \
} while (0)


Оптимизация «упакованная хэш-таблица»



Итак, вспомните о критически важном правиле: мы добавляем данные в arData в порядке возрастания, от нуля и до конца, а затем формируем вектор arData. Это позволяет легко и дёшево итерировать хэш-таблицу: достаточно просто итерировать С-массив arData. Поскольку в качестве ключа хэш-таблицы может быть использована строка или взятое не по порядку целочисленное значение, то нам нужна таблица преобразования для возможности проанализировать хэш.



Но в одном случае такие таблицы бесполезны: если пользователь применяет только целочисленные ключи и только в порядке возрастания. Тогда при просмотре arData от начала до конца мы получим данные в том же порядке, в котором они были помещены. А раз в этом случае таблица преобразования бесполезна, то можно и не размещать её в памяти.



Эта оптимизация называется «упакованная хэш-таблица» (packed hashtable):







Как видите, все ключи целочисленные и размещены просто в порядке возрастания. При итерировании arData[0] с самого начала мы будем получать элементы в правильной очерёдности. Так что таблица преобразования была уменьшена всего до двух ячеек, 2 * uint32 = 8 байт. Больше ячеек преобразования не нужно. Звучит странно, но с этими двумя ячейками производительность выше, чем совсем без них.



Должен вас предупредить: если вы нарушите правило, скажем, вставив строчный ключ (после хэширования/сжатия), то упакованную хэш-таблицу обязательно придётся преобразовать в классическую. С созданием всех необходимых ячеек преобразования и реорганизацией bucket’ов.



ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht)
{
void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
Bucket *old_buckets = ht->arData;

ht->u.flags &= ~HASH_FLAG_PACKED;
new_data = pemalloc(HT_SIZE_EX(ht->nTableSize, -ht->nTableSize), (ht)->u.flags & HASH_FLAG_PERSISTENT);
ht->nTableMask = -ht->nTableSize;
HT_SET_DATA_ADDR(ht, new_data);
memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
pefree(old_data, (ht)->u.flags & HASH_FLAG_PERSISTENT);
zend_hash_rehash(ht);
}


Хэш-таблица u.flags используется для того, чтобы определить, упакованная ли основная хэш-таблица. Если да, то её поведение будет отличаться от поведения традиционной хэш-таблицы, ей не понадобятся ячейки преобразования. В исходном коде вы можете найти немало мест, когда упакованные и классические хэши идут по разным ветвям кода. Например:



static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, zend_ulong h, zval *pData, uint32_t flag ZEND_FILE_LINE_DC)
{
uint32_t nIndex;
uint32_t idx;
Bucket *p;

/* ... */
if (UNEXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
CHECK_INIT(ht, h < ht->nTableSize);
if (h < ht->nTableSize) {
p = ht->arData + h;
goto add_to_packed;
}
goto add_to_hash;
} else if (ht->u.flags & HASH_FLAG_PACKED) {
/* ... */
} else if (EXPECTED(h < ht->nTableSize)) {
p = ht->arData + h;
} else if ((h >> 1) < ht->nTableSize &&
(ht->nTableSize >> 1) < ht->nNumOfElements) {
zend_hash_packed_grow(ht);
p = ht->arData + h;
} else {
goto convert_to_hash;
}
/* ... */


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




  • Память: разница в потреблении байт по сравнению с классической: (размер_таблицы - 2) * размер(uint32). Если у вас тысячи ячеек, то счёт идёт на килобайты.

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



Тем не менее если вы начнёте использовать строчные ключи или нарушите порядок сортировки целочисленных ключей (скажем, вставите 42 после 60), то хэш-таблицу придётся конвертировать в «классическую». На это вы потратите немного ресурсов процессора (на крупных массивах — несколько больше) и дополнительный объём памяти. Для создания упакованной хэш-таблицы достаточно сказать API:



void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed)


Имейте в виду, что zend_hash_real_init() — полный этап инициализации, а не «ленивый» (zend_hash_init()). Обычно, если инициализация «ленивая», то хэш-таблица изначально упакованная. А когда складываются соответствующие обстоятельства, она преобразуется в классический хэш.



Массивы в РНР



Давайте посмотрим, как можно проверить детали реализации хэш-таблиц в рамках пользовательского кода.



Память хэш-таблиц и оптимизация упакованных массивов



Сначала продемонстрируем оптимизацию упакованных массивов (packed array):



function m()
{
printf("%d\n", memory_get_usage());
}

$a = range(1,20000); /* range() создаёт упакованный массив */

m();

for($i=0; $i<5000; $i++) {
/* Продолжаем добавлять ключи в порядке возрастания,
* поэтому контракт упакованного массива всё ещё действителен:
* мы остаёмся в «упакованном» режиме */
$a[] = $i;
}

m();

/* Мы неожиданно нарушили контракт упакованного массива и заставили
* хэш-таблицу превратиться в «классическую», потратив больше
* памяти на ячейки преобразования */
$a['foo'] = 'bar';

m();


Как и ожидалось, результаты потребления памяти:



1406744
1406776
1533752


При переходе от упакованного массива к классическому хэшу потребление памяти выросло примерно на 130 Кб (для массива с 25 000 элементов).



Теперь продемонстрируем действие алгоритма уплотнения или увеличения:



function m()
{
printf("%d\n", memory_get_usage());
}

/* Размер хэш-таблицы соответствует двойке в степени. Давайте создадим
* массив из 32 768 ячеек (2^15). Используем упакованный массив */
for ($i=0; $i<32768; $i++) {
$a[$i] = $i;
}

m();

/* Теперь очистим его */
for ($i=0; $i<32768; $i++) {
unset($a[$i]);
}
m();

/* Добавим один элемент. Размер хэш-таблицы превысит предел,
* что запустит работу алгоритма уплотнения или увеличения */
$a[] = 42;

m();


Результаты:



1406864
1406896
1533872


Когда таблица заполнена и мы её опустошаем, потребляемая память не изменяется (шум по модулю, modulo noise). После применения unset() к каждому значению наша таблица будет иметь arData на 32 768 ячеек, заполненных UNDEF-zval’ами.



Теперь добавим что-нибудь в качестве следующего элемента. Помните nNumUsed, который используется для адресации arData и увеличивающийся при каждом добавлении? Теперь он переполнит размер таблицы, и нужно будет решать, уплотнять её или увеличивать размер.



Возможно ли уплотнение?



Ответ кажется очевидным — да, ведь у нас тут сплошные UNDEF-ячейки. Но на самом деле нет: нам нужно сохранять очерёдность данных, потому что мы используем упакованный массив и не хотели бы, чтобы он превратился в классический, пока нам совсем не припрёт. Если мы добавим элемент в уже существующую ячейку, то нарушим очерёдность, что запустит процедуру уплотнения, а не увеличения размера.



/* Код тот же, что и выше, но: */

/* здесь мы добавляем ещё один элемент к известному индексу (idx).
* Размер хэша переполнится, запустится алгоритм уплотнения или увеличения */
$a[3] = 42;

m();


Использование памяти:



1406864
1406896
1406896


Видите разницу? Теперь алгоритм не увеличил нашу таблицу с 32 768 до 65 538 ячеек, а уплотнил её. У таблицы в памяти всё ещё размещено 32 767 ячеек. А поскольку ячейка представляет собой Bucket, внутри которого находится zval, чей размер равен long (в нашем случае 42), то память не изменяется. Ведь в zval уже входит размер long. :) Следовательно, теперь мы можем повторно использовать эти 32 768 ячеек, поместив в них целочисленные значения, булевы, с плавающей запятой. А если в качестве значений мы используем строки, объекты, другие массивы и т. д., то выделится дополнительная память, указатель на которую будет храниться в предварительно размещённых в памяти UNDEF-zval’ах нашего «горячего» массива.



То же самое мы можем проделать с классической хэш-таблицей, просто используя строчные ключи. Когда мы переполним её на один элемент, таблица уплотнится, а не увеличится, потому что здесь не нужно сохранять очерёдность. Мы в любом случае находимся в «неупакованном» режиме, просто добавляем ещё одно значение на крайнюю левую позицию (idx 0), а все последующие — это UNDEF-zval.



function m()
{
printf("%d\n", memory_get_usage());
}

/* Размер хэш-таблицы соответствует двойке в степени. Давайте создадим
* массив из 32 768 ячеек (2^15). Здесь мы НЕ используем упакованный массив */
for ($i=0; $i<32768; $i++) {
/* возьмём строковый ключ, чтобы у нас был классический хэш */
$a[' ' . $i] = $i;
}

m();

/* теперь очистим его */
for ($i=0; $i<32768; $i++) {
unset($a[' ' . $i]);
}
m();

/* здесь мы добавляем ещё один элемент к известному индексу.
* Размер хэша переполнится, запустится алгоритм уплотнения или увеличения */
$a[] = 42;

m();


Результаты по памяти:



2582480
1533936
1533936


Как и ожидалось. Массив потребляет около 2,5 Мб. После применения unset() ко всем значениям потребление памяти снижается, мы освобождаем ключи. У нас 32 768 ключей zend_string, и после их освобождения массив начинает потреблять 1,5 Мб.



Если теперь добавить один элемент, то произойдёт переполнение внутреннего размера таблицы, что запустит алгоритм уплотнения или увеличения. Поскольку массив не упакованный, не нужно сохранять очерёдность элементов, и таблица будет уплотнена. Новое значение 42 записывается в idx 0, объём памяти не меняется. Конец истории.



Как видите, в некоторых редких случаях упакованные хэш-таблицы могут нам даже навредить, увеличившись в размере вместо уплотнения. Но разве в реальных проектах вы станете использовать такой глупый код? Это не должно влиять на повседневное программирование, но если вам нужна действительно высокая производительность (фреймворки, вы меня слышите?) и/или вы хотите оптимизировать высоконагруженные пакетные скрипты, то это может быть хорошим решением. Вместо того чтобы ломать голову над переходом к чистому С при решении подобных задач. Если у вас нет многих тысяч элементов, то потребление памяти будет смехотворным. Но в наших примерах было «всего» от 20 до 32 тысяч элементов, а разница измерялась в килобайтах и мегабайтах.



Неизменяемые массивы (Immutable arrays)



Неизменяемые массивы — это характерная особенность OPCache. Не включив OPCache, вы не сможете воспользоваться преимуществами неизменяемых массивов, т. е. доступных только для чтения. Если же OPCache активирован, то он просматривает содержимое каждого скрипта и пытается скопировать многие вещи в совместно используемую память. В частности, константные AST-ноды. Собственно, константный массив — это константная AST-нода. Например:



$a = ['foo', 1, 'bar'];


Здесь $a — константная AST-нода. Компилятор определяет, что это заполненный константами массив, и превращает его в константную ноду. Далее OPCache просматривает все скрипты в AST-ноде, копирует их связанный с процессом адрес (адрес кучи) в сегмент совместной используемой памяти, а затем этот адрес освобождает. Дополнительную информацию об OPCache вы можете найти в обзоре этого расширения.



Кроме того, OPCache превращает массивы в неизменяемые, устанавливая им флаги IS_ARRAY_IMMUTABLE и IS_TYPE_IMMUTABLE. Когда движок встречает IS_IMMUTABLE, он обращается с этими данными особым образом. Если вы преобразуете неизменяемый массив в переменную, то он не будет дуплицирован. В противном случае создаётся полная копия массива.



Пример описанной оптимизации:



$ar = [];
for ($i = 0; $i < 1000000; ++$i) {
$ar[] = [1, 2, 3, 4, 5, 6, 7, 8];
}


Этот скрипт потребляет около 400 Мб памяти без OPCache, а с ним — примерно 35 Мб. Если OPCache отключён, то в скрипте движок создаёт полную копию 8-элементного массива в каждом слоте $ar. В результате в памяти размещается один миллион 8-слотовых массивов. А если включить OPCache, то он пометит 8-слотовый массив как IS_IMMUTABLE и при запуске скрипта в слоты $ar будет копироваться всего лишь указатель массива, что предотвращает полное дуплицирование в каждом цикле.



Очевидно, что если позднее вы измените один из этих массивов, скажем, $ar[42][3] = 'foo';, то 8-слотовый массив в $ar[42] будет полностью дуплицирован механизмом копирования при записи.



Во внутренних хэш-таблицах была сделана ещё одна, практически случайная оптимизация. Как вы помните, PHP-массивы — всего лишь частный случай хэш-таблиц Zend. Поскольку вы в силах манипулировать ими посредством PHP, то можете представить и способ применения. Но хэш-таблицы используются по всему движку. Например, любой скрипт может содержать функции и/или классы. Эти две большие схемы хранятся в двух хэш-таблицах. Компилятор преобразует скрипт в так называемый OPArray и прикрепляет к нему таблицу функции (она может быть пустой) и таблицу класса (тоже может быть пустой). Когда PHP завершает текущий запрос, он очищает этот OPArray: уничтожает таблицы функции и класса. Но при включённом OPCache оба этих массива также становятся IMMUTABLE, и в результате движок не может их уничтожить. Как только появится следующий запрос, они сразу будут загружены из совместно используемой памяти и запросят точно такой же скрипт.



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



Но не дайте неизменяемым массивам вас запутать. К примеру, на данный момент подобный сценарий не имеет никакой оптимизации:



$a = [1];
$b = [1];


Здесь в памяти создаётся два массива. И нет процесса наподобие того, что используется для изолированных строк (interned strings), когда движок отслеживает каждую часть использованной строки, чтобы при необходимости использовать её снова. Неизменяемые массивы — это массивы, заполненные неизменяемыми типами (нет переменных, нет вызовов функций, всё решается во время компиляции), которые не дуплицируются в памяти при переносе из одного места в другое в ходе runtime’а PHP. Также эти типы никогда не уничтожаются в памяти (от одного запроса к другому). Кроме того, неизменяемые массивы используются только при активированном расширении OPCache.
Original source: habrahabr.ru.

https://habrahabr.ru/post/308240/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
kiev2376393

Украинская разработка завоевала рынок в Лондоне

Понедельник, 22 Августа 2016 г. 16:48 (ссылка)

Литр экономичного горючего, разработанного харьковчанами, позволяет преодолеть 570 км.

Читать далее...
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Обзор новых возможностей Mathematica 11 и языка Wolfram Language

Воскресенье, 21 Августа 2016 г. 11:42 (ссылка)



Перевод поста Стивен Вольфрам (Stephen Wolfram) "Today We Launch Version 11!".

Выражаю огромную благодарность Полине Сологуб за помощь в переводе и подготовке публикации

Содержание



Первое, что вы отметите...

3D печать

Машинное обучение и нейронные сети

Аудио

Встроенные данные о чем угодно: от скелетной структуры и продуктов питания до сведений о нашей Вселенной

Вычисления с реальными объектами

Передовые возможности географических вычислений и визуализаций

Не забудем про сложные задачи математического анализа и теоретической физики...

Образование

Совмещение всех функций в одно целое

Визуализация

От строк к тексту

Современный подход к программированию систем

Работа в интернете

Облачные данные

Подключайтесь к любым внешним сервисам: Facebook, Twitter, Instagram, ArXiv, Reddit и многим другим...

WolframScript

Новое в ядре языка Wolfram Language

И еще много нового...
Я рад объявить о выходе новой версии системы Mathematica и 11-й версии языка Wolfram Language, доступной как для Desktop-компьютеров, так и в облачном виде. В течение последних двух лет сотни человек упорно трудились над ее созданием, а несколько тысяч часов и я лично. Я очень взволнован; это важный шаг вперед, имеющий важное значение для многих крупнейших технологических областей.







Прошло больше 28 лет с тех пор, как вышла 1-я версия, — и почти 30 лет с тех пор, как я занялся ее разработкой. И все это время я продолжал воплощать дерзкую мечту — строить все больший и больший стек технологий. Большая часть программного обеспечения спустя несколько лет и несколько версий, за исключением мелких доработок, практически не меняется. С системой Mathematica и Wolfram Language сложилась совсем другая история: в течение трех десятилетий мы c каждой новой версией продвигались вперед, завоевывая постепенно огромное количество новых областей.



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



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



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



Много лет мы называли нашу основную систему Mathematica. Однако после того, как мы добавили новые направления, а также вышли далеко за пределы вещей, связанных каким-либо образом с «математикой», мы решили ввести понятие Wolfram Language, чтобы передать суть всего того, что мы делаем. И сейчас Wolfram Language определяет работу не только системы Mathematica, но также и Wolfram Development Platform и Wolfram Programming Lab, а также других продуктов и платформ. И благодаря единству нашей разработоки программного обеспечения мы можем сегодня выпустить 11-ю версию всех наших систем, основанных на Wolfram Language.







ОК, так что же такого грандиозного и нового в 11-й версии? Ну, мы подготовили даже не одно большое нововведение, а много. Чтобы вы могли оценить масштаб: мы добавили 555 совершенно новых функций, представляющих огромное количество новых функциональных возможностей (по сравнению с первой версией, в которой была 551 функция в общей сложности). На самом деле, в данное число функций не включены даже углубленные варианты многих существующих функций.



Мы управляем развитием компании таким образом, что у нас всегда есть портфель актуальных проектов — от достаточно мелких до тех, которые могут потребовать на реализацию пять или более лет. 11-я версия включает в себя результаты нескольких пятилетних проектов. Мы заинтересованы в том, чтобы как можно быстрее доносить результаты наших исследований до пользователей, так что с момента выхода 10-й версии у нас было несколько промежуточных релизов; в результате 11-я версия фактически представляет собой сочетание многих совершенно новых разработок, которые мы уже анонсировали в версиях 10.1, 10.2, 10.3 и 10.4 (многие функции, которые были помечены как «экспериментальные» в 10.x выпусках, доступны теперь полностью в версии 11.0).



Первое, что вы отметите...



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



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







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



3D печать



В 11-й версии появилось много новых областей. Давайте перейдем к одной из них: 3D-печати. Впервые я напечатал в 3D еще в 2002 году. В течение многих лет мы экспортировали в STL. Для 11-й версии мы создали полный цикл, который начинается от создания 3D-геометрии и заканчивается в тот момент, когда готовое изделие лежит на вашем 3D-принтере.







Раньше мне хотелось взять чертеж в 3D и просто распечатать его — тоже в 3D. И иногда мне везло и это было легко осуществить. Но в большинстве случаев это неудобно и сложно, поскольку графики, которые высвечиваются на экране, не обязательно соответствуют геометрии, которая может быть напечатана на 3D-принтере. Так что удобно настроить или исправить геометрию так, чтобы она действительно работала на 3D-принтере, оказывается трудной задачей (и да — если вы сделаете это неправильно, из принтера может брызнуть пластик).



В 11-й версии стало возможным взять любой 3D-график и просто напечатать его в 3D. Или вы можете получить структуру молекулы или перепад высот вокруг горы, и тоже просто напечатать их в 3D. За эти годы я много печатал в 3D, и каждый раз это было своего рода маленькое приключение. Но теперь, благодаря 11-й версии, легко распечатать в 3D практически все, что угодно. И теперь, когда я думаю об этом, мне кажется нужна 3D-распечатка того, как рос Wolfram Language по разным областям



Машинное обучение и нейронные сети



В некотором смысле, Mathematica и Wolfram Language всегда занимались искусственным интеллектом (ИИ). На протяжении многих лет мы были пионерами в решении серии проблем, связанных с ИИ: от математического решения автоматизации эстетики до понимания естественного языка. Но еще в 10-й версии мы сделали большой шаг вперед в отношении машинного обучения — речь идет о разработке максимально автоматизированных главных функций (Classify и Predict).



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



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



В 11-ю версию встроены такие функции, как ImageIdentify, которая идентифицирует более 10000 различных видов объектов. Благодаря общей структуре довольно просто взять уже выделенные характеристики и использовать их для обучения новых классификаторов изображений более эффективно, чем раньше.







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



Ну хорошо — допустим, вы тоже хотите заняться этим. Да, вы можете что-то собирать с помощью целого ряда низкоуровневых библиотек. Но при создании 11-й версии мы поставили перед собой цель создать оптимизированный символьный способ настройки и обучения нейросетей, в которых должно быть автоматизировано как можно больше из того, что они в принципе могут делать. В 11-й версии есть теперь функции вроде NetGraph и NetChain наряду со всевозможными “специальными функциями нейронных сетей” типа DotPlusLayer или ConvolutionLayer. И с помощью этих функций можно взять новейшие сети и быстро настроить их в Wolfram Language (рекуррентные сети в 11-й версии пока не совсем доделаны, однако они появятся в ближайшее время).







Конечно, все это хорошо работает благодаря интеграции с Wolfram Language. Нейронная сеть — это просто Graph объект, как и любой другой. И такие вводимые данные, как изображения или тексты, могут немедленно и автоматически обрабатываться с помощью стандартных возможностей Wolfram Language в формах, подходящих для нейросетевых вычислений.



Из их названия («нейронные сети») кажется, что они связаны с мозгом. Однако на самом деле это совершенно общие вычислительные структуры: они соответствуют сложным комбинациям простых функций. Они не связаны с теми простейшими программами, которые я так долго изучал, хотя и обладают специальными характеристиками, настроенными так, чтобы легко обучаться на примерах.



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



Я надеюсь, что мы сможем ускорить этот процесс с помощью 11-й версии. Нам удалось сделать «нейросетевое программирование» просто еще одной парадигмой программирования, наравне с остальными интегрированной в Wolfram Language. Это очень результативно и позволяет взаимодействовать с огромными обучающими последвательностями. Но в конечном итоге, думаю, самая сильная вещь — это то, что оно сразу же вписывается во все, что делает Wolfram Language. Даже в 11-й версии мы уже использовали его при работе с нашими внутренними алгоритмами в таких областях, обработка изображений, сигналов и текста. История «нейросетевого программирования» еще очень молода, и я рад, что Wolfram Language будет играть центральную роль в ней.



Аудио



Хорошо, давайте обратимся к еще одной новой области 11-й версии: аудио. Наша цель — иметь возможность обрабатывать любые данные непосредственно в Wolfram Language. У нас уже есть такие объекты, как графические примитивы (из которых можно строить сколь угодно сложную векторную 2D и 3D графику), растровые изображения (также 2D и 3D), геометрические вычисляемые объекты, графы и сети, формулы и множество других функций, последовательно представленных в языке в виде символьных структур. И, начиная с 11-й версии, теперь стал доступен еще один тип данных — аудио.







С аудио работать сложно из-за его размера. Однако в 11-й версии можно беспрепятственно обработать, скажем, час аудио непосредственно в Wolfram Language. Безусловно, за этим стоят технологии кэширования, потоковой передачи данных и так далее. Но это все автоматизировано — и в языке это просто объект, задаваемый функцией Audio. Этот Audio-объект можно сразу же подвергнуть крайне сложной обработке и анализу, которые доступны только в Wolfram Language.



Встроенные данные о чем угодно: от скелетной структуры и продуктов питания до сведений о нашей Вселенной



Wolfram Language — язык, основанный на знаниях. А это значит, что в него встроено множество знаний — как о вычислениях, так и обо всем в мире. На сегодняшний день Wolfram Language охватывает тысячи областей знаний реального мира — начиная от стран и фильмов до компаний и планет. Всё новые данные поступают в главную облачную базу знаний Wolfram, и мы тщательно курируем данные о том новом, что появляется в мире (кто знал, например, что в Австрии появились недавно новые административные подразделения?). Многие эти данные доступны в Wolfram|Alpha. Но только в Wolfram Language данные оживают для полноценных вычислений — и тогда становятся очевидными все те усилия, которые мы вкладываем в обеспечение согласованности и последовательности всей системы.



Мы постоянно работаем над тем, чтобы расширить сферу знаний, которые охватываются Wolfram Language. Те несколько направлений, над которыми мы работали на протяжении многих лет, в 11-й версии, наконец, готовы к работе. Особенно сложно было с данными по анатомии. Однако в 11-й версии вы увидите подробные 3D-модели всех значимых структур человеческого тела. Вы сможете увидеть, как сложные кости ноги прилегают друг к другу. И вы сможете сделать расчеты. Или напечатать их в 3D. Вы сможете изучить сеть артерий вокруг сердца. Должен сказать, что, когда я исследовал это, я более чем когда-либо был поражен уровнем морфологической сложности, присущей человеческому телу. Однако теперь мы можем легко делать расчеты в этой области. Для их обслуживания появились новые, и, возможно, неожиданные функции, — такие, как AnatomyPlot3D. Безусловно, с ними еще придется поработать: к примеру, наши анатомические данные — это только данные "среднего взрослого мужчины", а суставы не могут двигаться, и т. д.







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







Еще одна долгожданная новинка — исторические данные по странам. У нас имелись данные о странах в новейшее время (как правило, с 1960-х или 1970-х гг.). А что насчет более раннего периода? Что насчет Пруссии? Что можно сказать о Римской империи? В общем, в 11-й версии мы наконец получили по крайней мере приблизительную информацию о границах всех серьезных стран на протяжении всей истории. Таким образом, посредством Wolfram Language можно сделать расчеты относительно подъема и падения империй.







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







Еще одна удобная функция из 11-й версии — WikipediaData, которая по запросу выдает любую статью из Википедии (или различные виды данных, которые в ней содержатся). Также есть функция WolframLanguageData, которая позволяет получать вычисляемые данные о самом языке Wolfram: примеры, приведенные в документации, связи между функциями и так далее.



Во многих сферах мы в основном имеем дело со статическими данными ("Какова плотность золота?"; "Каким было население Лондона в 1959 году?"). Но есть и другие сферы, в которых интерес к статическим данным невелик. В 11-й версии есть несколько новых примеров. Например, данные о смертности людей (“Какова вероятность умереть в промежутке между возрастом X и возрастом Y?”), стандартные данные об океанах (“Какое давление на глубине X?”), данные о энергии торможения радиоактивных частиц в веществе (radioactive stopping power) и данные о росте человека — а также сведения о всей Вселенной согласно стандартной космологической модели.



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







Вычисления с реальными объектами



Одной из самых мощных возможностей Wolfram Language является его способность производить непосредственные вычисления с реальными объектами. США, или Россия, или даже ящерица, — для Wolfram Language это просто объекты, которыми можно манипулировать в качестве символьных конструкций с использованием общей символьной парадигмы языка. Сами объекты не имеют значения; они просто символы. Но их свойства могут иметь значения, скажем, вычисление свойства «Population» у entity [[USA]] даст 322 млн.



Однако давайте предположим, что мы хотим не просто взять некоторый объект (например, США) и найти значение его свойств. Скажем, вместо этого мы хотим найти объекты с определенными свойствами и значениями. Скажем, мы хотим найти 5 крупнейших стран в мире по численности населения. В 11-й версии мы можем сделать это по-новому. Вместо того, чтобы указать конкретный явный объект, мы указываем вычисление, которое неявно определяет класс объектов. Так, например, мы можем получить список 5 крупнейших по численности населения стран вот так:







TakeLargest[5]операторная форма новой функции, работающий с самыми большими элементами в списке. Неявные объекты в конечном итоге часто используют операторные формы — как в запросах Dataset. И в некотором смысле они используют символьный характер Wolfram Language, потому что они работают с функциями, которые определяют их как данные.



Механизм объектов и свойств и неявных объектов работает для всех различных типов объектов, которые существуют в языке Wolfram. Однако начиная с 11-й версии он не ограничивается встроенными типами объектов. Новая конструкция, которая называется EntityStore, позволяет определять собственные типы объектов, а также указывать их свойства и значения и так далее, а затем использовать их в любых вычислениях.



Подобно тому, как Dataset является мощным иерархическим обобщением типичных понятий баз данных, EntityStore является своего рода символическим обобщением типичной реляционной базы данных. И если вы создали сложный объект EntityStore, вы можете просто использовать функцию CloudDeploy, чтобы развернуть его в облаке и использовать, когда захотите.



Передовые возможности географических вычислений и визуализаций



География — один из аспектов «знаний о реальном мире». Но Wolfram Language не просто имеет доступ к подробным географическим данным (для Земли, Луны, Марса и даже Плутона); он может также осуществлять вычисления с этими данными. В его арсенале — колоссальная коллекция мгновенно вычислимых гео проекций и все настройки для поддержки детальной геодезии. Помните сферическую тригонометрию? Wolfram Language не только знает, что Земля представляет собой сферу, но и, используя фактическую форму Земли, правильно вычисляет расстояния, строит области и так далее на карте с произвольными проекциями.







Если говорить о создании карт, то Wolfram Language теперь имеет доступ не только к карте улиц всего мира, но и к таким вещам, как исторические границы стран, а также к спутниковым снимкам, — с низким разрешением (как минимум). С учетом карт улиц появляется новый важный класс возможных вычислений: туристических направленийвремени поездки) откуда угодно и куда угодно.







Не забудем про сложные задачи математического анализа и теоретической физики...



В 11-й версии во всех областях Wolfram Language появилось много новых возможностей. Однако они доступны теперь и в сферах, традиционных для Mathematica, — например, математического анализа.



Например, есть функции (вроде DEigenvalues) для работы с собственными числами и функциями дифференциальных операторов для обыкновенных дифференциальных уравнений и уравнений в частных производных. Чтобы сделать это возможным, необходим огромный стек алгоритмических технологий, и мы уже более 25 лет движемся в эту сторону. Важно, что это не тот случай, когда необходимо ставить ту или иную задачу с помощью детально проработанных знаний численного анализа. Здесь вы определяете только уравнения и их граничные условия — и система автоматически выясняет, как их решить.







Еще в 1976 году я написал программу на Fortran, предназначенную для поиска собственных функций уравнения Шредингера для физики элементарных частиц, которую я изучал. В 1981 году я писал программы на C, чтобы сделать то же самое для некоторых уравнений релятивистской квантовой механики. Я терпеливо ждал того дня, когда я смогу просто вбить эти задачи и сразу же получить ответы. И сейчас это стало возможным.



Конечно, в 11-й версии все это носит гораздо более общий характер. Я имел дело с простыми граничными условиями; но в 11-й версии для того, чтобы настроить граничные условия, можно использовать весь геометрический подъязык Wolfram Language — и все данные, которые нам доступны. Так что найти собственные поверхности «барабана» любой формы — например, в форме США, — совсем несложно.







В данном случае у нас нет никакого выбора, кроме как делать все вычисления численно. Тем не менее, в 11-й версии скоро станет возможным искать собственные числа и функции дифференциальных операторов аналитически. В 11-ю версию также добавлены некоторые новые возможности для решения в общем виде (аналитически) дифференциальных уравнений в частных производных. В частности, у нас был большой проект R&D, который подвел нас теперь к той точке, в которой мы можем найти символьное решение практически для любого дифференциального уравнения в частных производных, которое может появиться в любой научной работе или учебнике (если оно вообще имеет аналитическое решение).



Еще в 1979 году, когда я создал предшественника системы Mathematica (см. пост на Хабре "Краткая история появления Mathematica"), я составил список вещей, которые, как я надеялся, мы бы в конце концов в состоянии были бы осуществить. Один из пунктов этого списка — решение интегральных уравнений. Что ж — рад сообщить, что 37 лет спустя, мы, наконец, получили стек алгоритмических технологий, позволяющий сделать это возможным, и в 11-й версии представлены решения многих классов интегро-дифференциальных уравнений.



Есть и еще кое-что — например, построение функций Грина для дифференциальных уравнений. И, наконец, долгожданная (по крайней мере, для меня) функция интегрального преобразования Меллина (оно было моим фаворитом с тех пор, как стало центром моей статьи по физике частиц в 1977 году).



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



Образование



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



В качестве одного из примеров можно привести автоматическую обработку разрывов, асимптот и т. д. на графиках функций. Так что теперь, к примеру, функция Tan[х] строится идеальным «школьным» способом, без разрывов от –

https://habrahabr.ru/post/308168/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Зачем нам jQuery?

Пятница, 19 Августа 2016 г. 18:00 (ссылка)

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





Чтобы пояснить, чем она нас привлекает в эпоху Node и ES6 (у нас в ассортименте и этого добра навалом) предлагаем познакомиться со статьей Коди Линдли, вышедшей вскоре после вышеупомянутого третьего издания



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



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



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



Что такое jQuery?



jQuery – это библиотека JavaScript (т.e., она написана на JavaScript), предназначенная для абстрагирования, выравнивания, исправления и упрощения скриптинга при работе с узлами HTML-элементов в браузере или для работы в браузере без графического интерфейса.



Итак:





Все это оборачивается в более простой и менее корявый API, нежели нативный API DOM – вот вам и библиотека jQuery.



Теперь позвольте объяснить, что я понимаю под “скриптингом HTML-элементов”. При помощи jQuery совершенно тривиально решаются задачи вроде «визуально скрыть второй элемент h2 в документе .html. Код jQuery для такой задачи выглядел бы так:



jQuery('h2:eq(1)').hide();


Давайте немного разберем эту строку с кодом jQuery. Сначала вызывается функция jQuery(), ей мы передаем специальный CSS-селектор библиотеки jQuery, выбирающий второй элемент h2 в HTML-документе. Затем вызывается метод jQuery.hide(), скрывающий элемент h2. Вот как прост и семантически выразителен код jQuery.



Теперь сравним его с нативным DOM-кодом, который потребовалось бы написать, если бы мы не работали с jQuery.



document.querySelectorAll('h2')[1].style.setProperty('display','none');


Какой вариант было бы удобнее написать? А читать, а отлаживать? Также учтите, что вышеприведенный нативный DOM-код предполагает, что все браузеры поддерживают использованные здесь методы DOM. Однако оказывается, что некоторые старые браузеры не поддерживают querySelectorAll() или setProperty(). Поэтому вышеприведенный код jQuery вполне нормально работал бы в IE8, а нативный код DOM вызвал бы ошибку JavaScript. Но, все-таки, даже если бы обе строки кода работали повсюду, какую из них было бы проще читать и писать?



Имея дело с jQuery, можно не беспокоиться о том, какой браузер что поддерживает, либо какой DOM API в каком браузере может забарахлить. Работая с jQuery, вы сможете работать быстрее решать задачи на более простом коде, и при этом не беспокоиться, так как jQuery абстрагирует за вас многие проблемы.



jQuery = JavaScript?



Поскольку jQuery повсеместно распространена, то вы, возможно, не вполне представляете, где заканчивается JavaScript и начинается jQuery. Для многих веб-дизайнеров и начинающих разработчиков HTML/CSS, библиотека jQuery — это первый контакт с языком программирования JavaScript. Поэтому jQuery иногда путают с JavaScript.



Первым делом давайте оговоримся, что JavaScript – это не jQuery и даже не сам DOM API. jQuery – это сторонняя свободная библиотека, написанная на JavaScript и поддерживаемая целым сообществом разработчиков. Кроме того, jQuery не относится к числу стандартов тех организаций (напр., W3C), которые пишут спецификации HTML, CSS или DOM.



Не забывайте, что jQuery служит прежде всего как «сахар» и используется поверх DOM API. Этот сахар помогает работать с интерфейсом DOM, который печально известен своей сложностью и обилием багов.



jQuery – это просто полезная библиотека, которой можно пользоваться при написании сценариев для HTML-элементов. На практике большинство разработчиков прибегают к ней при DOM-скриптинге, поскольку ее API позволяет решить больше задач меньшим количеством кода.



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



Два краеугольных камня jQuery



Две базовые концепции, на которых основана jQuery, таковы: “найди и сделай” и “пиши меньше, делай больше.”

Две этих концепции можно развернуть и переформулировать в виде следующего утверждения: первоочередная задача jQuery заключается в организации выбора (то есть, нахождении) или в создании HTML-элементов для решения практических задач. Без jQuery для этого потребовалось бы больше кода и больше сноровки в обращении с DOM. Достаточно вспомнить рассмотренный выше пример с сокрытием элемента h2.



Следует отметить, что круг возможностей jQuery этим не ограничивается. Она не просто абстрагирует нативные DOM-взаимодействия, но и абстрагирует асинхронные HTTP-запросы (т.н. AJAX) при помощи объекта XMLHttpRequest. Кроме того, в ней есть еще ряд вспомогательных решений на JavaScript и мелких инструментов. Но основная польза jQuery заключается именно в упрощении HTML-скриптинга и просто в том, что с ней приятно работать.



Еще добавлю, что польза jQuery – не в успешном устранении браузерных багов. «Краеугольные камни» нисколько не связаны с этими аспектами jQuery. В долгосрочном отношении самая сильная сторона jQuery заключается в том, что ее API абстрагирует DOM. И эта ценность никуда не денется.



Как jQuery сочетается с современной веб-разработкой



Библиотеке jQuery уже десять лет. Она создавалась для той эпохи веб-разработки, которую мы уже безусловно миновали. jQuery не является незаменимой технологией для работы с DOM или выполнения асинхронных HTTP-запросов. Практически все, что можно сделать с jQuery, можно сделать и без нее. А если вас интересует всего лишь пара мелких простых взаимодействий с DOM в одном-двух современных браузерах, то возможно, лучше будет воспользоваться нативными DOM-методами, а не jQuery.



Однако, при любых разработках, связанных с BOM (браузерной моделью документа) или DOM, а не только с косметическими взаимодействиями, следует пользоваться jQuery. В противном случае вы станете изобретать велосипед (т.e. элементы абстракций jQuery), а потом испытывать его на всевозможных дорожках (т.e в мобильных браузерах и браузерах для ПК).



Опытные разработчики знают, что такое «стоять на плечах гигантов», и когда следует избегать излишней сложности. В большинстве случаев нам все равно не обойтись без jQuery, когда нужно в сжатые сроки выполнить нетривиальную работу, связанную с HTML и DOM.



Кроме того, даже если бы jQuery не решала ни единой проблемы с DOM или с разношерстными браузерными реализациями спецификации DOM, важность самого API ничуть бы не уменьшилась, поскольку он так удобен для HTML-скриптинга.



Причем jQuery совершенствуется, и с ее помощью программисты могут работать толковее и быстрее. Такова ситуация сегодня, так было и на момент создания библиотеки. Сказать «мне не нужна jQuery» — все равно, что утверждать «обойдусь без lo-dash или underscore.js». Разумеется, можно без них обойтись. Но об их ценности судят не по этому.

Их ценность — в API. Из-за излишней сложности разработка может замедляться. Поэтому нам и нравятся такие вещи как lo-dash и jQuery – с ними все упрощается. А поскольку jQuery облегчает выполнение сложных задач (например, писать сценарии для HTML), она не устареет.



Если вы по-прежнему сомневаетесь, нужна ли jQuery в современной веб-разработке, предлагаю посмотреть следующую презентацию от одного из разработчиков библиотеки, где он обосновывает ее нужность безотносительно наворотов современных браузеров.



Приложение – важные факты об jQuery



Наконец, перечислю некоторые важные факты, касающиеся jQuery.




  • Библиотеку jQuery написал Джон Резиг (John Resig), ее релиз состоялся 26 августа 2006. Джон признавался, что написал этот код, чтобы “произвести революцию во взаимодействии JavaScript с HTML”.

  • jQuery считается наиболее популярной и востребованной современной библиотекой JavaScript.

  • jQuery – свободное ПО, предоставляемое по лицензии MIT.

  • Существует две версии jQuery. Версия 1.x поддерживает Internet Explorer 6, 7 и 8\, а 2.x поддерживает только IE9+. Если вам требуется поддержка IE8, то придется работать с версией 1.x. Но это нормально, обе версии по-прежнему активно разрабатываются.

  • Минимальная версия jQuery 2.x имеет размер 82kb. В архиве Gzip — около 28k.

  • Минимальная версия jQuery 1.x имеет размер 96kb. В архиве Gzip — около 32k.

  • Исходный код jQuery доступен на Github.

  • На основе исходного кода с Github можно создать собственную версию jQuery.

  • jQuery можно установить при помощи менеджера пакетов bower или npm (т.e. $ bower install jquery or npm install jquery).

  • У jQuery есть официальная сеть CDN, в которой можно взять различные версии jQuery.

  • jQuery имеет простую архитектуру на основе плагинов, поэтому любой желающий может добавлять в нее собственные методы.

  • jQuery обладает обширным набором плагинов. Можно приобрести высококачественные плагины для enterprise-разработки (напр. Kendo UI) или воспользоваться не менее классными бесплатными (напр. Bootstrap).

  • jQuery можно разбить на категории (в соответствии с разбиением документации API ).




  • ajax

  • attributes

  • callbacks object

  • core

  • CSS

  • data

  • deferred object

  • dimensions

  • effects

  • events

  • forms

  • internals

  • manipulation

  • miscellaneous

  • offset

  • properties

  • selectors

  • traversing

  • utilities





Актуальность книги






































Проголосовало 95 человек. Воздержалось 35 человек.





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


Original source: habrahabr.ru.

https://habrahabr.ru/post/308134/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Основы геймдизайна: 20 настольных игр. Часть седьмая и последняя: головоломки Nikoli, кроссворды

Четверг, 18 Августа 2016 г. 16:54 (ссылка)

В статье Джона Харриса из серии «Основы геймдизайна» представлен подробный обзор самых популярных настольных игр, включая традиционные вроде шахмат и го, ролевые вроде «Зова Ктулху», европейские вроде «Колонизаторов» и многие другие, у которых есть чему поучиться.







Предыдущие статьи из серии:



Го, шахматы, парчиси.

Нарды, монополия, скрэббл.

Пенте, Колонизаторы, Пуэрто-Рико.

Билет на поезд, Каркассон, Колонизаторы.

Манчкин, Контрактный бридж, Ужас Аркхэма.

Ядерная война, Паранойя, Зов Ктулху.



Головоломки Nikoli





Издатель: Nikoli

Тип: логические головоломки для решения в одиночку

Уровень сложности: от умеренного до высокого

Создатели: разные авторы

Фактор везения: отсутствует

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








Обзор



Японский журнал головоломок Nikoli славится своими интересными логическими задачами. И хоть он и не является непосредственным «родителем» головоломок судоку, именно благодаря ему они приобрели такую популярность. Кроме них, в журнале можно найти множество отличных головоломок на любой вкус. Все они представляют собой схему, которую нужно дополнить тем или иным образом в соответствии с поставленной задачей.



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



Всем хорошо известны головоломки судоку. Для примера задач такого типа я остановился на менее популярной головоломке под названием «Китайская стена», которая в журнале Nikoli называется Slitherlink («скользящие линии»). Мне кажется, что ей также подходит название Loopy («петляющая»), которое упоминается в собрании головоломок Саймона Татэма (Tatham's Puzzle Collection). Иллюстрации, которые я привожу в этой статье, взяты из адаптированной компьютерной версии, но логика у них точно такая же, как и в «бумажном» первоисточнике. Каждая из головоломок, которые публикуют в Nikoli, хороша по-своему, но «Китайская стена» видится мне наиболее подходящей для знакомства с ними.

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



Рассмотрим одну легкую задачу из собрания головоломок Саймона Татэма. При должной практике её можно решить менее чем за 3 минуты. Что о ней можно сказать?







Во-первых, квадраты с цифрой 0 не должны иметь видимых границ. Квадраты с цифрой 3, напротив, должны быть обведены с трех сторон. Поскольку «троечка» предполагает только одну пустую границу, квадрат с цифрой 3 в верхнем правом углу следует обвести по краю – здесь мы может провести либо 2 угловые линии, либо ни одной.



Менее очевидный момент: линии должны присутствовать между двумя прилегающими «троечками»; кроме того, еще одну линию следует провести по любой из сторон этой «пары». Линия должна быть непрерывной, без каких-либо ответвлений; если линия огибает квадрат под прямым углом, остальные 2 стороны квадрата должны быть пустыми. Всё это, вкупе с очевидными правилами касательно «единичек», «двоечек» и прилегающих линий, позволяет нам начать решать задачу следующим образом:







Заполнить всю сетку, отталкиваясь от этих набросков, достаточно легко. Более сложные задачи требуют, в свою очередь, более глубокого понимания структуры головоломки, использования закономерностей высокого порядка (таких как, например, замкнутость петли в «Китайской стене») и постоянных проб и ошибок.



Всё вышесказанное относится к судоку в частности и большинству головоломок Nikoli в целом. По сути, эти задачи однотипны. Как только вы поймете методику решения головоломок одного типа, сможете решать подобные задачи вплоть до среднего уровня сложности. Чтобы продвинуться дальше, вам понадобится приложить больше усилий. Но помните – даже эксперты не могут сходу решать судоку сложных уровней.







Что можно почерпнуть из этих головоломок?



Особый интерес для разработчиков игр представляют головоломки, которые были успешно адаптированы до компьютерных версий. Помимо того, что судоку играет ключевую роль в сайд-стори серии игр Brain Age от Nintendo, приложение с открытым кодом Simon Tatham Puzzle Collection объединяет игровые модули множества головоломок и весьма достойные генераторы случайных задач в одном пакете.



На страницах Nikoli можно найти множество идей для головоломок, которые сейчас очень популярны. Этим, а также превосходной коллекцией судоку и интерфейсом для их решения в значительной мере объясняется популярность серии игр Brain Age. Кроме того, элементы головоломок можно использовать в качестве мини-игр в составе игр других жанров.



Дополнительные материалы:



Сейчас Nikoli поддерживает англоязычный сайт с головоломками и инструкциями по их решению.

Долгое время ведущим периодическим изданием, посвященным головоломкам, для англоговорящей аудитории был Games Magazine, открывший миру Уилла Шортца. В каждом выпуске есть страницы с головоломками, среди которых можно найти задачи по типу тех, что публикует Nikoli.



Кроссворды





Традиционная головоломка

Тип: словесная головоломка для одного игрока

Уровень сложности: от низкого до высокого

Создатель: Артур Уинн (авторство более ранней версии приписывается Джузеппе Айрольди)

Фактор везения: отсутствует

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








Обзор



К сожалению, кроссворды не пользуются особой популярностью среди геймеров. Но на самом деле это весьма непростые и очень увлекательные головоломки, для решения которых требуется начитанность, богатый словарный запас, гибкий ум, а в некоторых случаях – нестандартное мышление. Даже самые эрудированные люди ломают голову над ежедневными кроссвордами от New York Times. Начиная с понедельника, уровень сложности становится всё выше, а в воскресенье читателей ждет самое настоящее испытание.

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



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



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



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



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



Иногда можно встретить и другие уловки. Если в определении вы видите нетипичное употребление слов, будьте начеку: ответ может быть сформулирован аналогичным образом. Также стоит учитывать, что в традиционных кроссвордах, как правило, запрещено использовать одно слово несколько раз – даже в другой грамматической форме или в определении к другому слову.







Стоит отметить, что в британских кроссвордах, которые в США называют «криптограммами», используется намного больше уловок, чем в американских аналогах. Умение обходить эти подводные камни приходит с опытом, и затем они уже не представляют особой сложности.

За последние годы появилось много тематических кроссвордов, которые при грамотном подходе могут давать игроку дополнительные подсказки. Если у кроссворда есть заголовок, значит все ответы объединены общей темой. Но иногда даже в безымянных кроссвордах тему можно узнать всего по нескольким определениям. Как правило, самые длинные слова в кроссворде (особенно если они растянуты по всей длине поля) заключают в себе прямое указание на общую тему.



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

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



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







Что можно почерпнуть из этих головоломок?



На консоли Nintendo DS есть как минимум 3 сборника кроссвордов: от изданий New York Times (рекомендуется) и USA Today (достойно внимания), а также коллекция оригинальных кроссвордов (последний сборник был выпущен Nintendo и, как ни странно, выглядит весьма удручающе на фоне остальных игр этой компании). К тому же, совсем недавно стал доступен для скачивания еще один сборник кроссвордов для консоли DSi.



Но не стоит думать, что приложение с кроссвордами принесет вам легкие деньги. Составить такую головоломку под силу далеко не каждому. Большинство кроссвордов в печатных и цифровых изданиях имеют на удивление узкий круг авторов, которые часто используют для этого специальное ПО. Если вы начнете по-настоящему увлекаться кроссвордами, со временем вам наверняка станут известны имена некоторых составителей, и вы даже сможете различать их стили.



Многие игры требуют от игроков определенных знаний. Хороший тому пример – настольная игра «Скрэббл», которая переняла много механик у кроссвордов. Однако некоторые разработчики стремятся исключить этот фактор из своих игр.

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



Примечание



Правила в некоторых вышеперечисленных играх (особенно в европейских настольных играх в предыдущих статьях) могут отличаться в зависимости от издания или страны выпуска. Поэтому при описании всех игр я руководствовался правилами из последних американских изданий.



Написав эту статью, я нашел отличную серию видеороликов под названием Board Games With Scott («Настольные игры со Скоттом»). Если вы хотите больше узнать о настольных играх, в частности о «Билете на поезд» и «Пуэрто-Рико», видеообзоры Скотта Николсона – это именно то, что вам нужно.



И напоследок напомню, что лучшее место для знакомства с настольными играми – сайт BoardGameGeek. Это крупнейший интернет-портал, посвященный настольным играм, на котором вы можете найти ответы на все интересующие вас вопросы.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/307926/

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Java Stream API: что делает хорошо, а что не очень?

Среда, 17 Августа 2016 г. 16:02 (ссылка)





Настолько ли «энергичен» Java 8 Stream API? Возможно ли «превращение» обработки сложных операций над коллекциями в простой и понятный код? Где та выгода от параллельных операций, и когда стоит остановиться? Это одни из многочисленных вопросов, встречающихся читателям. Попробуем разобрать подводные камни Stream API с Тагиром Валеевым aka @lany. Многие читатели уже знакомы с нашим собеседником по статьям, исследованиям в области Java, выразительным докладам на конференциях. Итак, без проволочек, начинаем обсуждение.



Тагир, у вас отличные показатели на ресурсе StackOverflow (gold status в ветке «java-stream»). Как вы думаете, динамика применения Java 8 Stream API и сложность конструкций выросла (на основе вопросов и ответов на данном ресурсе)?



— Верно, одно время я много времени проводил на StackOverflow, постоянно отслеживая вопросы по Stream API. Сейчас заглядываю периодически, так как, на мой взгляд, на большинство интересных вопросов уже есть ответы. Безусловно, чувствуется, что люди распробовали Stream API, было бы странно, если бы это было не так. Первые вопросы по этой теме появились ещё до выпуска Java-8, когда люди экспериментировали с ранними сборками. Расцвет пришёлся на конец 2014 и 2015-й год.



Многие интересные вопросы связаны не только с тем, что можно сделать со Stream API, но и с тем, чего нормально сделать нельзя без сторонних библиотек. Пользователи, постоянно спрашивая и обсуждая, стремились раздвинуть рамки Stream API. Некоторые из этих вопросов послужили источниками идей для моей библиотеки StreamEx, расширяющей функциональность Java 8 Stream API.



Вы упомянули про StreamEx. Расскажите, что побудило вас к созданию? Какие цели вы преследовали?



— Мотивы были сугубо практические. Когда на работе мы перешли на Java 8, первая эйфория от красоты и удобства довольно быстро сменилась чередой спотыканий: хотелось сделать с помощью Stream API определённые вещи, которые вроде делаться должны, но по факту не получались. Приходилось удлинять код или отступать от спецификации. Я начал добавлять в рабочие проекты вспомогательные классы и методы для решения данных проблем, но выглядело это некрасиво. Потом я догадался обернуть стандартные стримы в свои классы, которые предлагают ряд дополнительных операций, и работать стало существенно приятнее. Эти классы я выделил в отдельный открытый проект и начал развивать его.



На ваш взгляд, какие виды расчетов и операций и над какими данными действительно стоит реализовать c использованием Stream API, а что не очень подходит для обработки?



— Stream API любит неизменяемые данные. Если вы хотите поменять существующие структуры данных, а не создать новые, вам нужно что-то другое. Посмотрите в сторону новых стандартных методов (например, List.replaceAll).



Stream API любит независимые данные. Если для получения результата вам нужно использовать одновременно несколько элементов из входного набора, без сторонних библиотек будет очень коряво. Но библиотеки вроде StreamEx часто решают эту проблему.



Stream API любит решать одну задачу за проход. Если вы хотите в один обход данных решить несколько разных задач, готовьтесь писать свои коллекторы. И не факт, что это вообще получится.



Stream API не любит проверяемые исключения. Вам будет не очень удобно кидать их из операций Stream API. Опять же есть библиотеки, которые пытаются это облегчить (скажем, jOO

https://habrahabr.ru/post/307938/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
quitbet

Разработка дизайна футболок

Понедельник, 17 Августа 2015 г. 08:20 (ссылка)

Разработка дизайна футболок.


Читать далее
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Дино Эспозито на uDev Tech Events: «Life as a Service: Scalability and other Aspects»

Вторник, 16 Августа 2016 г. 19:15 (ссылка)

13 сентября 2016 года Дино Эспозито выступит на uDev Tech Events с лекцией на тему «Life as a Service: Scalability and other Aspects». Для тех, кто не сможет посетить мероприятие, будет вестись прямая трансляция.











Дино Эспозито – IT-специалист с мировым именем, автор популярных книг по .NET-программированию издательства Microsoft Press, эксперт по разработке веб- и мобильных приложений. Его работы помогли профессиональному росту тысяч разработчиков и архитекторов.



В своем выступлении на uDev Tech Events Дино Эспозито расскажет о конкретных способах улучшения масштабируемости приложений и устранения проблем производительности программного обеспечения.



Ивент пройдет по адресу: Харьков, пр-т Независимости, 2, Premier Palace Hotel Kharkiv.

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



uDev Tech Events – это бесплатные технические ивенты для разработчиков, на которых обсуждают методологии, рассказывают о новых инструментах и перенимают опыт лучших специалистов IT-индустрии.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/307900/

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[recovery mode] CSS наших дней

Вторник, 16 Августа 2016 г. 06:29 (ссылка)

Лучшие практики стилизации элементов сейчас можно выразить следующими тезисами:




  1. Старайтесь стилизовать элементы так, чтобы их визуализация не ломалась при перемещении их в другое место. Из этого принципа следует, что стоит минимизировать использование составных селекторов (например, вида header p a).

  2. Используйте пространства имён, чтобы минимизировать вероятность конфликтов правил относящихся к разным элементам. Это приводит к длинным именам, но избавляет от кучи проблем в будущем.



Итак, какие возможности для привязки стилей к элементам у нас есть сейчас? У любого элемента есть следующие характеристики:




  1. Имя элемента

  2. Идентификатор

  3. Набор классов

  4. Набор атрибутов

  5. Набор свойств



Давайте посмотрим, как мы можем их использовать, на примере простого списка задач, содержащего карточки с названием задачи и оценкой времени её выполнения...



Имя элемента





Write HTML
1 hour


Write CSS
2 hours


Write JS
10 hours



my-task-list {
display: flex;
flex-direction: column;
}

my-task-card {
display: inline-flex;
margin: .5rem;
padding: .5rem;
border: 2px solid gray;
border-radius: .5rem;
}

my-task-card-title {
margin: .5rem;
font-weight: bolder;
flex: 1 1 auto;
}

my-task-card-estimate {
margin: .5rem;
font-weight: lighter;
}


http://liveweave.com/XjrwY5



Как видим, код CSS получился довольно простым, но длинные имена тегов весьма перегружают HTML. Кроме того, имена элементов отлично подходят для указания имени блока, но совершенно не позволяют добавлять элементу модификаторов (например, чтобы как-то выделить завершённые или важные задачи). Но самая главная проблема кастомизированных имён элементов в том, что иногда имя элемента должно быть строго определённым. Например, элемент video для вставки видео на страницу, или элемент a для создания гиперссылки.



Данный способ стилизации почти не используется ввиду упомянутых ограничений. Лишь новички и энтузиасты от web components практикуют эту технику.



Идентификаторы





#my-task-list {
display: flex;
flex-direction: column;
}

#my-task-card {
display: inline-flex;
margin: .5rem;
padding: .5rem;
border: 2px solid gray;
border-radius: .5rem;
text-decoration: inherit;
color: inherit;
}

#my-task-card-title {
margin: .5rem;
font-weight: bolder;
flex: 1 1 auto;
}

#my-task-card-estimate {
margin: .5rem;
font-weight: lighter;
}


http://liveweave.com/ccrFOf



Код CSS почти не изменился — мы лишь добавили решётки перед именами. Код HTML стал гораздо легче за счёт отсутствия повторения длинных имён. Имена тегов теперь могут быть произвольными, так что мы с лёгкостью превратили карточки задач в гиперссылки, ведущие на страницы с подробностями по этим задачам. Тем не менее, у нас по прежнему нет поддержки модификаторов. И кроме того, идеологически не верно иметь на одной странице несколько элементов с одинаковым идентификатором, хотя это ничего и не ломает.



Раньше идентификаторы пользовались популярностью для стилизации, но сейчас их полностью вытеснили классы.



Классы





.my-task-list {
display: flex;
flex-direction: column;
}

.my-task-card {
display: inline-flex;
margin: .5rem;
padding: .5rem;
border: 2px solid gray;
border-radius: .5rem;
text-decoration: inherit;
color: inherit;
}

.my-task-card_important {
border-color: red;
}

.my-task-card_completed {
opacity: .5;
}

.my-task-card-title {
margin: .5rem;
font-weight: bolder;
flex: 1 1 auto;
}

.my-task-card-estimate {
margin: .5rem;
font-weight: lighter;
}


http://liveweave.com/qZDyzV



Изменения как в CSS, так и в HTML — незначительные, но классов на один элемент можно навешивать куда больше одного, чем мы и воспользовались, дополнительно раскрасив важные и завершённые задачи. Правда, за счёт пространств имён, такие модификаторы довольно сильно раздувают HTML.



99% кода сейчас в вебе написано на классах, тем не менее есть решение лучше..



Атрибуты





[my-task-list] {
display: flex;
flex-direction: column;
}

[my-task-card] {
display: inline-flex;
margin: .5rem;
padding: .5rem;
border: 2px solid gray;
border-radius: .5rem;
text-decoration: none;
color: inherit;
}

[my-task-card~=important] {
border-color: red;
}

[my-task-card~=completed] {
opacity: .5;
}

[my-task-card-title] {
margin: .5rem;
font-weight: bolder;
flex: 1 1 auto;
}

[my-task-card-estimate] {
margin: .5rem;
font-weight: lighter;
}


http://liveweave.com/8ddncZ



Код CSS усложнился незначительно, зато HTML, не смотря на поддержку модификаторов, стал куда более простым. Тут мы используем специальный селектор, который буквально означает "применить такие-то стили к элементу, у которого в таком-то атрибуте, среди разделённых пробелом слов, есть такое-то".



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



Свойства



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



[my-task-card]:hover {
border-color: steelblue;
box-shadow: 0 0 .5rem rgba(0,0,255,.25);
opacity: 1;
}


К сожалению набор псевдоклассов никак от нас не зависит, поэтому, если, например, нам потребуется выделить карточку текущей открытой задачи, то нам уже придётся использовать модификатор:



[my-task-card]:not([my-task-card~=current]):hover {
border-color: steelblue;
box-shadow: 0 0 .5rem rgba(0,0,255,.25);
opacity: 1;
}

[my-task-card~=current] {
background: #eee;
border: none;
}


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



[my-task-card~=hover] {
border-color: steelblue;
box-shadow: 0 0 .5rem rgba(0,0,255,.25);
opacity: 1;
}

[my-task-card~=current] {
background: #eee;
border: none;
}






http://liveweave.com/1GJrUM



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



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


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/307824/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Основы геймдизайна: 20 настольных игр. Часть шестая: Ядерная война, Паранойя, Зов Ктулху

Понедельник, 15 Августа 2016 г. 16:20 (ссылка)

В статье Джона Харриса из серии «Основы геймдизайна» представлен подробный обзор самых популярных настольных игр, включая традиционные вроде шахмат и го, ролевые вроде «Зова Ктулху», европейские вроде «Колонизаторов» и многие другие, у которых есть чему поучиться.





Ядерная война



Издатель: «Flying Buffalo»

Тип: агрессивная карточная игра на ядерно-военную тематику для 2-6 игроков

Уровень сложности: очень низкий

Создатель: Дуглас Малевицки

Фактор везения: высокий





Описание:



Игроки выступают в роли глав ядерных держав. Есть 2 типа атаки, применимые в двух различных фазах игры. Один из них добавляет ходившему игроку население, но может использоваться только в мирное время.

Для использования другого типа понадобится 2 хода, но он развязывает войну, в состоянии которой первый тип атаки просто неприменим. Лишившись населения, игрок выбывает из основной игры, но у него появляется последний шанс на возмездие. Также существует вероятность того, что в игре не будет победителей.



Обзор игры



По сравнению с предыдущими играми эта настолько проста, что все её правила можно уместить в этой статье.

В игре есть 2 типа карт: карты с населением и карты приказов. В самом начале карты с населением раздаются всем игрокам. Каждая из карт может содержать от 1 до 25 миллионов человек. Население нации равнозначно очкам здоровья: стоит им закончиться, как вы сразу же проигрываете. Количество карт с населением известно, но сама численность населения скрыта от игроков, так что они не знают, сколько еще смогут продержаться их противники и у кого самое выгодное положение.



Во время игры можно заменять неиспользуемые карты с населением на карты из колоды, если кто-то теряет меньшее количество населения, чем указано на карте. Также игрокам раздаются карты приказов. Некоторые из этих карт содержат определенное действие, отмеченное грифом «Секретно». Если такую карту вытягивает один из игроков, он показывает её всем. Карта сразу же отыгрывается, а взамен игрок получает другую карту из колоды. (Тот факт, что секреты становятся моментально известными всем игрокам, является саркастическим намеком на происходящие в мире события). Секрет либо сразу дает игроку большой прирост населения, либо уничтожает людей в огромных количествах.

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



Кроме Секретов, есть еще 4 вида карт. Карты Пропаганды являются самыми простыми в использовании. Когда игрок получает такую карту, он может мгновенно присвоить себе часть населения своего противника. Эти карты очень сильные, и, за исключением случайных Секретов, пропаганда является единственным способом увеличить свое население, но она работает лишь в мирное время.

Мирное время заканчивается в случае проведения успешной атаки, которая активируется двумя другими видами карт – Ракетами и Боеголовками. Эти карты используются в сочетании друг с другом, по очереди за 2 идущих подряд хода. Например, сначала запускается Ракета, а Боеголовка, подходящая для этой ракеты (это указано на карте Ракеты), запускается в следующий ход.



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



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



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

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



Что можно почерпнуть из этой игры?



«Ядерная война» – это быстрая и веселая игра, не требующая серьезного стратегического мышления. Очевидно, в основном из-за развеселой апокалиптической тематики. Игра была создана в 1960-х, в разгар гонки вооружений между США и СССР, а в то время стандарты геймдизайна были не такими уж высокими по современным меркам.

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



В игре есть несколько очень хороших механик. Например, возможность скрывать настоящее количество населения – отличное средство для снижения эффективности атак, направленных на уничтожение конкретных игроков.

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



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



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

Трудно как-либо охарактеризовать стратегию «Ядерной войны», ведь по сути она ограничивается нападением на игроков, которые либо больше всех этого заслуживают, либо лидируют, либо просто ведут себя по-скотски. Странная тематика игры не особо работает в её пользу, но она всё равно гораздо популярнее таких игр как «Уно».



Паранойя



Издатель: впервые выпущена издательством West End Games, затем переиздана Mongoose Publishing

Тип: антиутопическая ролевая игра для трех и более игроков

Уровень сложности: умеренный

Создатели: Грег Костикян, Дэн Гербер, Эрик Голдберг, Аллен Варни и др.

Фактор везения: умеренный





Описание



Ролевая игра, в которой игроки выступают в роли агентов авторитарного города-государства Альфа Комплекс – в лучших традициях оруэлловского «1984». Задача этих, как правило, вооруженных до зубов ищеек – бороться с подпольными диверсионными группировками и любыми проявлениями инакомыслия.

Несмотря на мрачную атмосферу, «Паранойя» – чрезвычайно веселая и увлекательная игра. Всё потому, что в ходе зачистки маргинальных элементов ваша основная цель – как можно сильнее насолить соперникам.



Обзор игры



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



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

Смерть поджидает агентов на каждом шагу – как при выполнении заданий, так и от рук других игроков. Поэтому изначально у каждого есть несколько жизней, или «клонов». Иногда агент просто не в состоянии выполнить миссию, а учитывая, что поражение приравнивается к измене, вам необходимо найти себе оправдание – например, свалив вину на одного из соперников.



Суть игры в следующем. Комплексом Альфа управляет деспотичный искусственный интеллект – Компьютер. В роли его агентов игроки должны разыскивать и ликвидировать предателей, поэтому все задания так или иначе связаны с этой целью. Самый злостный враг системы – коммунисты, но в игре есть и другие преступные группировки.



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



Фишка в том, что Компьютер не подозревает о существовании предателей среди своих агентов. Не стоит забывать, что благодаря тайной полиции он держит в ежовых рукавицах весь город, а с его кровожадностью не могла бы сравниться даже GLaDOS из вселенной Half-Life. Поэтому, если вам дорога ваша жизнь, вам лучше не привлекать к себе внимание. Кроме того, борьба с инакомыслием – один из немногих способов продвинуться вверх по социальной иерархии в мире игры. И, как нарочно, потенциальные предатели есть прямо у вас под боком – это ваши соперники.



Задания, которые игроки получают от своих тайных организаций, связаны с устранением других агентов или саботажем официальных миссий. При этом, будучи мутантами, они могут использовать свои уникальные способности – прямо как Люди Икс. Главное – не делать это в открытую, если не хотите подписать себе смертный приговор. Точно так же нельзя просто взять и пристрелить кого-нибудь – сперва нужно найти доказательства измены. Именно это отличает «Паранойю» от игр, где нужно уничтожать соперников всеми способами.

Самое главное оружие в игре – знание. Проходя инструктаж в своей организации, каждый игрок узнает что-то (о подполье, суперсилах или предателях), чего не знают его соперники. Запутанная сеть взаимосвязанных, часто противоречивых заданий, возможностей и опасностей делает игру необычайно интересной – тем более для опытных игроков. Как ни странно, при этом многие считают «Паранойю» несерьезной и поверхностной игрой. Но при наличии опытного гейммастера это совсем не так.



Возможно, «Паранойя» получила такую репутацию из-за того, что персонажи постоянно умирают. Уличить соперника в предательстве можно просто на ровном месте и так же просто пристрелить его. Хочется кого-то убить – пожалуйста. В конце концов, у агентов есть 5 запасных жизней, так почему бы и нет? В связи с этим у персонажей нет очков здоровья, но зато есть состояния: «здоров», «оглушен», «ранен», «обезврежен», «мертв» и «испепелен» – просто шутки ради. В общем, внезапная смерть в игре – обычное дело.

В последних изданиях «Паранойи» появилась новая интересная механика – так называемые «очки искажения», наподобие «заслуг» в классической ролевой настолке «Охотники на привидений». Эти пункты в виде круглых фишек присуждает ведущий в качестве поощрения за хороший отыгрыш, а игроки могут использовать их по своему усмотрению.



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

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

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



Что можно почерпнуть из этой игры?



Удивительно, что таких RPG-игр, как «Паранойя», где игроки не столько помогают друг другу, сколько соперничают, не так уж и много. Запомните мои слова: компания, которая первой адаптирует «Паранойю» или похожую на нее игру в жанр MMORPG, войдет в историю. Многопользовательские шутеры от первого лица – неплохое начало, но противостояние в них слишком прямолинейно, чтобы создать подходящую параноидальную атмосферу. Похожий геймплей можно выделить разве что в классе Шпиона из игры Team Fortress.

Атмосфера игры во многом зависит от игроков. Последние редакции «Паранойи» включают 3 стиля: «Пиф-паф», «Классический» и «Правильный» – по возрастанию серьезности.



Большинство любителей игры отдают предпочтение классическим правилам, но те, кто знаком с игрой лишь понаслышке или играл всего пару раз, думают, что «Паранойя» сводится к «Пиф-паф» – постоянным перестрелкам мультяшных персонажей. Правильная «Паранойя» – самая серьезная, занимает много времени и больше напоминает традиционную RPG, но в своей строгости впадает в крайность – как и «Пиф-паф» в своей излишней свободе.



Зов Ктулху



Издатель: Chaosium, Inc.

Тип: ролевой хоррор по мотивам произведений Говарда Лавкрафта для двух и более игроков

Уровень сложности: умеренный

Создатели: Сэнди Петерсен и Линн Уиллис

Фактор везения: умеренно низкий





Описание



Ролевая игра, в которой игроки по очереди расследуют таинственные события, сталкиваясь с жуткими силами Мифов Ктулху – древними богоподобными существами, ни в грош не ставящими человеческий род. Игра не такая фатальная, как «Паранойя», но всё же довольно беспощадна.



Обзор игры



Это вторая и последняя полноценная ролевая игра, представленная в этом списке.

Говард Филлис Лавкрафт в 1920-1930 годах работал в жанре хоррора. Будучи убежденным атеистом, Лавкрафт остро ощущал бессмысленность и мелочность человеческой жизни в контексте непостижимой бесконечности вселенной, и его творчество насквозь пропитано этой философией.



У Лавкрафта было множество друзей, и вместе они создали одну из самых популярных вселенных в литературе – «Мифы Ктулху», т. е. целый пантеон жутких потусторонних божеств, наделенных неведомой силой. Мифы Ктулху разрослись так широко, что на их основе появилась целая ролевая игра – «Зов Ктулху».



Несмотря на то, что своими временными корнями игра упирается в эпоху расцвета «Подземелий и Драконов» и содержит определенные сходства с ней (например, важность характеристик персонажа), между ними существуют различия.

Во-первых, в «Зове Ктулху» отсутствует система опыта. Лучший способ прокачаться – это набить как можно больше очков навыков. Во-вторых, многие монстры в принципе неубиваемы. В компьютерных ролевых играх, особенно в японских, очень распространено клише: на протяжение всей игры вы пытаетесь помешать чудовищному колдуну пробудить древнее божество для властвования над землей, но он всё же преуспевает в своем замысле, а вам приходится сражаться с его божеством, как говорится, голыми руками.



Схожесть с Мифами Ктулху настолько велика, что пространства для случайности практически не остается, но все же в «Зове Ктулху» события развиваются немного иначе. Высшая сущность, сам Великий Ктулху, за один раунд может автоматически поглотить до 3 человек, и это только разминка. Уничтожить его тоже не выйдет: через полчаса он восстановится с полными силами.



Характеристики этих монстров в основном перечислены в книге, так что гейммастер может с точностью выложить проигрывающим игрокам, что их ждет, ведь при встрече с Ктулху, Йог-Сототом или, упаси Бог, Азатотом лучший выбор – это бегство. В адаптации игры по системе d20 эти характеристики вообще не представлены, поскольку вполне закономерно предполагается, что появление этих существ в игре просто означает начало конца для игроков. Чтобы победить в «Зове Ктулху», нужно остановить культ еще до того, как они разбудят Ктулху.



В отличие от большинства ролевых игр (в том числе empowerment fantasy), в «Зове Ктулху» персонажи со временем часто теряют свою силу. По сути, цель Мифов Ктулху – показать нам, что какими бы магическими силами не овладевали люди, для богов они всё равно останутся мелкими букашками.



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



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

К тому же, ради различных видов заклинаний приходится жертвовать силой (POW), а это перманентный показатель. Благодаря этому персонажи в игре не имеют возможности стремительно прокачаться только за счет приключений; наоборот, у них очень непродолжительный срок жизни, что парадоксально делает этот хоррор одним из лучших аналогов RPG в реальной жизни.



Что можно почерпнуть из этой игры?



В «Зове Ктулху» самое интересное заключается не в сражениях с потусторонними монстрами и отчаянных попытках не сойти с ума, хотя и они являются довольно забавными. Но сражения, по сути, в большинстве своем смертельны, а лишиться рассудка, скорее всего, придется так или иначе по ходу расследования.



Именно расследование – главное средство вовлечения в игровой процесс. Множество сценариев начинаются с так называемой фазы расследования, когда, получив задание, игроки исследуют все возможное и невозможное перед тем, как встретиться с опасностью один на один.



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



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



Игра не только завораживает увлекательной информацией, а иногда и магией, необходимой для победы над врагом, но и создает великолепную атмосферу обыденного мира 1920-х, в котором постепенно раскрываются подробности странных и ужасных событий. Например, дом, который принадлежит одному семейству вот уже 400 лет… Его владельцами всегда были люди с одинаковыми именами, которые всегда выглядели где-то на 40 лет, и за всё время так и не удалось найти ни одного свидетельства об их смерти...


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/307776/

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Прогрессивная загрузка web-приложения с помощью разделения кода

Понедельник, 15 Августа 2016 г. 10:32 (ссылка)

В этой статье мы рассмотрим как можно ускорить первоночальную загрузку web-приложения c помощью разделения кода (code splitting). Для реализации задуманного я буду использовать webpack v1, а для демонстрации — React (не обязателен).



В большинстве своих проектов я собираю все javascript файлы (а иногда css и картинки тоже) в ОДИН ОЧЕНЬ БОЛЬШОЙ bundle.js. Возможно ты, дорогой читатель, делаешь точно так же. Это достаточно стандартная практика для современных веб-приложений.



Но этот подход имеет один (и иногда достаточно важный) недостаток: первоночальная загрузка приложения может занимать очень долгое время, так как web-браузер должен (1) загрузить огромный файл и (2) распарсить тонну js-кода. Загрузка файла может занять долгое время, если у пользователя медленный интернет. Так же этот огромный файл может содержать код компонентов, которые пользователь НИКОГДА не увидит (например, пользователь просто не откроет некоторые части вашего приложения).



Что делать?



Прогрессивная загрузка



Одно из решений для лучшего UX — это Progressive Web App. Если термин не знаком, предлагаю его быстренько погуглить, можно найти множество хороших видео и статей. Так Progressive Web App содержит в себе много интересных идей, но сейчас я хочу сфокусироваться только на Progressive Loading (Прогрессивная загрузка).



Идея прогрессивной загрузки достаточно простая:

1. Сделать первоначальную загрузку как можно быстрее

2. Загружать UI компоненты только по мере надобности



Предположим у нас есть некоторое React приложение, которое рисует график на странице:



// App.js
import React from 'react';

import LineChart from './LineChart';
import BarChart from './BarChart';

export default class App extends React.Component {
// не показываем графики при первой загрузке
state = {
showCharts: false
};

// показываем или скрываем графики
handleChange = () => {
this.setState({
showCharts: !this.state.showCharts
});
}

render() {
return (

Show charts:


{
this.state.showCharts ?

: null
}

);
}
}


Компонет для отрисовки графика очень простой:



// LineChart.js

import React from 'react';
import {Stage, Layer, Line} from 'react-konva';

export default () => (





);




// BarChart.js

import React from 'react';
import {Stage, Layer, Rect} from 'react-konva';

export default () => (






);


Подобные графики могут быть очень тяжёлыми. В данном примере каждый из них имеет react-konva в зависимостях (а также же konva, как зависимость react-konva).



Обратите внимание, что графики LineChart и BarChart не видны при первой загрузке. Для того, чтобы их увидел пользователь, ему необходимо отметить checkbox:



image



Возможно пользователь никогда не тыкнет checkbox. И это достаточно частая ситуация в реальных и больших приложениях, когда пользователь обращается не ко всем частям приложения или открывает их только через некоторое время. То с текущим подходом мы компонуем все зависимости в один файл. В данном случае мы имеем: корневой компонент App, React, компоненты графиков, react-konva, konva.



Собранный и минифицированный результат:



image



Использование сети во время загрузки:



image



280кб для bundle.js и 3.5 секунды для первоночальной загрузки на 3g соединении.



Реализация прогрессивной загрузки



Как можно удалить компоненты графиков из budle.js и загрузить их позже, тем самый сделав первоночальную зугрузку намного быстрее? Скажем привет старому доброму AMD (asynchronous module definition)! Так же Webpack имеет хорошую поддержку code splitting (разделение кода).



Я предлагаю реализовать HOC (higher order component, он же компонент высшего порядка), который загрузит код графика только тогда, когда компонент будет установлен в DOM (используем componentDidMount):



// LineChartAsync.js

import React from 'react';

export default class AsyncComponent extends React.Component {
state = {
component: null
}
componentDidMount() {
// загружаем компонент при установлении в DOM
require.ensure([], (require) => {
// !Важно! Мы не может здесь использовать конструкцию вида:
// require(this.props.path).default;
// Потому, что webpack не сможет статически анализировать такой код
// поэтому нужно явно импортировать необходимый модуль
const Component = require('./LineChart').default;
this.setState({
component: Component
});
});
}
render() {
if (this.state.component) {
return
}
return (
Loading
);
}
}


Далее, вместо того, чтобы писать:



import LineChart from ‘./LineChart’;


Будем писать:



import LineChart from ‘./LineChartAsync’;


Посмотрим, что мы имеем после сборки:



image



У нас есть bundle.js, который содержит в себе компонент App и React.



Файлы 1.bundle.js и 2.bundle.js сгенерированы webpack'ом и включают в себя LineChart и BarChart. Но постойте, почему суммарный размер файлов стал больше? 143kb+143kb+147kb = 433kb. В предыдущем подходе было только 280kb. Всё потому, что зависимости LineChart и BarChart включены ДВАЖДЫ (react-konva и konva определены и в 1.bundle.js, и в 2.bundle.js). Мы может это исправить с помощью webpack.optimize.CommonsChunkPlugin:



new webpack.optimize.CommonsChunkPlugin({
children: true,
async: true,
}),


Так мы получим:



image



Теперь зависимости LineChart и BarChart перемещены в отдельный файл 3.bundle.js, и суммарный размер остаётся практически прежним — 289kb:



Использование сети при первой загруке:



image



Использование сети после показа графиков:



image



Теперь мы имеем 1.75 секунд для первоночальной загрузки. Это уже намного лучше чем 3.5 секунд.



Рефакторинг



Чтобы сделать код несколько лучше, я предлагаю немного переписать LineChartAsync и BarChartAsync. Сначала определим базовый компонент AsyncComponent:



// AsyncComponent.js

import React from 'react';

export default class AsyncComponent extends React.Component {
state = {
component: null
}
componentDidMount() {
this.props.loader((componentModule) => {
this.setState({
component: componentModule.default
});
});
}
renderPlaceholder() {
return
Loading
;
}
render() {
if (this.state.component) {
return
}
return (this.props.renderPlaceholder || this.renderPlaceholder)();
}
}

AsyncComponent.propTypes = {
loader: React.PropTypes.func.isRequired,
renderPlaceholder: React.PropTypes.func
};


Далее BarChartAsync (и LineChartAsync) могут быть переписанны в более простые компоненты:



// BarChartAsync.js

import React from 'react';
import AsyncComponent from './AsyncComponent';

const loader = (cb) => {
require.ensure([], (require) => {
cb(require('./BarChart'))
});
}

export default (props) =>


Но мы можем ЕЩЕ улучшить прогрессивную загрузку. Как только приложение первоначально загрузилось, мы можем загружать дополнительные компоненты в фоновом режиме. Возможно, они будут загружены до того, как пользователь отметит checkbox.



// BarChartAsync.js

import React from 'react';
import AsyncComponent from './AsyncComponent';
import sceduleLoad from './loader';

const loader = (cb) => {
require.ensure([], (require) => {
cb(require('./BarChart'))
});
}

sceduleLoad(loader);

export default (props) =>



И loader.js будет выглядеть примерно так:



const queue = [];
const delay = 300;

let isWaiting = false;

function requestLoad() {
if (isWaiting) {
return;
}
if (!queue.length) {
return;
}
const loader = queue.pop();
isWaiting = true;
loader(() => {
setTimeout(() => {
isWaiting = false;
requestLoad();
}, delay)
});
}

export default function sceduleLoad(loader) {
queue.push(loader);
requestLoad();
}


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



image



Отмечу, что этот прогресс-бар сделан не для вызова API, а именно для загрузки самого модуля (его код и код его зависимостей).



const renderPlaceholder = () =>




export default (props) =>



Заключение



В результате наших улучшений мы получаем:



1. Первоночальный bundle.js имеет меньший размер. А это значит, что пользователь увидит на экране что-то осмысленное намного раньше;

2. Дополнительные компоненты могут бы загруженны асинхронно в фоне;

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

4. Для точно такой же реализации понадобится webpack. React я использовал в качестве примера, подобное решение можно использовать и с другими фреймворами/библиотеками.



Полный исходный код примера и конфигурационные файлы можно глянуть тут: https://github.com/lavrton/Progressive-Web-App-Loading.
Original source: habrahabr.ru.

https://habrahabr.ru/post/307694/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

HelloHome Case Study

Пятница, 12 Августа 2016 г. 15:46 (ссылка)

image

HelloHome Case Study



HelloHome – приложение для поиска соседей созданное по модели Tinder.



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



Основные проблемы в этой сфере были и остаются следующие:




  1. Ниша узкая, на рынке не много предложений.




  2. Как правило, поиск соседа это лотерея, никогда не знаешь какой человек тебе попадется, пока не пообщаешься или не поживешь с ним лично. Эта проблема значительно затягивает поиск во времени.



В приложении HelloHome мы постарались решить эти основные проблемы.



image

HelloHome — Light VS Dark



1. Проблема которую решает приложение



Существует достаточное количество приложений, сервисов и сайтов, но у них всех много похожих проблем:



— небольшое количество анкет;

— анкеты содержат мало информации о владельце;

— сервисы больше похожи на доски объявлений чем на сервисы поиска людей;

— сложный поиск который не сохраняет запросы пользователя;

— сложность коммуникации, как правило в сервисах нет встроенных месенджеров;

— необходимость самому постоянно находится в поиске;

— отсутствие уведомлений о новых и подходящих анкетах.


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



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

— единожды задав поиск, приложение будет всегда искать подходящие анкеты;

— анкеты достаточно подробные чтобы сложилось первое впечатление о человеке;

— со всеми подходящими претендентами можно быстро пообщаться в месенджере и принять решение, которого будет достаточно для того чтобы встретится и пообщаться в жизни;

— приложение будет уведомлять вас при появлении новых анкет или анкет соответствующих вашему поиску.


image

Скриншот с экранов совпадения



2. Структура приложения



Приложение состоит из двух видов пользователей, людей которые ищут квартиру и владельцев квартир, которые ищут себе соседа. Соответственно в HelloHome нужно было сделать два типа анкет, в отличии от Tinder в котором реализован только один тип анкет для всех пользователей. Эти факторы нужно было учитывать при проектировании приложения. В процессе проектирования мы постарались учесть все детали, вы можете увидеть это в прототипе на invisionapp.



image

Скриншот карточки апартаментов и пользователя



3. Проектирование



К сожалению в этом проекте мы не делали полноценный прототип в Flinto или Pixate, поэтому для нас важно было качественно было проработать детали в invisionapp. Нужно было чтобы в приложении даже без динамики просматривалась общая логика, а также были спроектированы все мелкие сценарии.



Особенности приложения



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



image

Скриншот с шагами в wireframes с воронкой регистрации




  1. Несколько раз на этапе проектирования и дизайна была переделана и реализована страница анкеты квартиры



image

Скриншот с несколькими вариантами анкет



4. Дизайн



Было принято решение сделать уникальный дизайн который бы запоминался и ассоциировался именно с этим приложением. Нужно было сделать что-то свое и чтобы дизайн не выглядел 1 в 1 как Tinder. Это решение аргументировалось тем, что стиль приложения должен отложиться в голове у пользователя, а если пользователь будет делиться приложением с друзьями, то в двух словах это должно было звучать примерно так «Это как Tinder только для поиска соседей». Также мы решили немного отойти от гайдлайнов.



Long Shadow



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



image

Скриншот с Long Shadow



Воронка регистрации



Как мы писали ранее, у нас получилась длинная воронка регистрации.



image

Скриншот с воронкой регистрации



Tour Screens & Sucsess Screens



Для того чтобы пользователь не сильно раздражался по поводу регистрации, мы решили вставить еще несколько экранов в виде Tour Screens.



image

Tour screens for HelloHome



Которые бы дали начальную цель, а также парочку Succsess Screens для поднятия настроения.



image

Скриншот с Succsess screens



Сейчас насколько эффективно это решение сказать к сожалению не можем, но тесты покажут и мы поправим этот момент уже во второй версии.



Amenities



Поскольку заказчик знал много об информации о своих будущих пользователях, он помог нам составить подробный спиcок amenities для анкет.



image

Скриншот с Amenities



5. Заключение



Изначально предполагалось сделать 8 экранов, в последствии приложение разрослось до 53.



Собственно что получилось можете увидеть в нашем проекте на invisionapp и dribble.



Сейчас проект находится на стадии разработки у девелоперов и как только будет готов мы проанонсируем его у нас в Twitter и Facebook.



Будем рады услышать от вас фидбек.





Насколько вам интересны подобные статьи
















































Проголосовало 8 человек. Воздержалось 2 человека.





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


Original source: habrahabr.ru.

https://habrahabr.ru/post/307646/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<разработка - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda