Страницы

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

пятница, 26 октября 2018 г.

Как грамотно составить Roadmap

Есть у меня идея, на мой взгляд, хорошая. Но, как справедливо заметил товарищ Сиверс, идеи ничего не стоят без реализации. Идея довольно сложная с точки зрения программирования, и я пока ещё ничего сложнее 500 строк кода не писал, но всё случается в первый раз. На данном этапе я занимаюсь (громко сказано) разработкой архитектуры (ещё громче сказано) программы: разбиваю основную задачу на подзадачи и составляю график выполнения работ. Вот тут-то и начинаются проблемы. Если с разбиением я худо-бедно справляюсь (на бумаге составил граф, что с чем связано), то вот с графиком у меня проблемы: хочется взяться за всё и сразу, а так нельзя. Нужен совет более опытного мастера. Ну и вообще любая помощь приветствуется. Ещё два месяца спустя Желание реализовать эту задумку не утихает, скорее даже наоборот: зреет и крепнет. За это время со мной много чего случилось и не всегда было время на то, чтобы исследовать панду и заниматься проектом. Но я всё-таки успел пройти туториал на MyGameFast (к сожалению, незавершённое автором), наговнокодить змейку, которая вполне себе играбельная. Правда, в ней нет ни меню, ни возможности изменить скорость, ни таблицы результатов, в неё можно просто играть. Сейчас думаю над тем, стоит ли приводить её в более опрятный (с точки зрения кода и особенностей панды) вид или бросить её как есть и начать вплотную работать уже с Mad Lab Худо-бедно список фич, которые должны быть в версии 0.1 я набросал, но прежде всего нужно грамотно определить общую архитектуру программы с учётом особенностей панды, чтобы потом всё заново переписывать не пришлось. UPD OCT Отчёт за сентябрь Отчёт за октябрь Долго думал выкладывать тут эти ссылки или нет. Если прочитаете, то поймёте почему. Даже не знаю, буду ли выкладывать тут отчёт за ноябрь. В этом вопросе, пожалуй, лучше уже ничего не писать, если вдруг захотите со мной связаться, то пишите в джаббер: montreal@jabber.ru или на почту, указанную в профиле. Лучше будет сразу указать причину, по которой пишете. UPD NOV Отчёт за ноябрь. Точнее, ноябрьский отчёт за октябрь. Написан, разумеется, в большей степени для себя и там вы найдёте все мои мысли о прошедшем месяце. PS Не знаю, насколько всё это соответствует правилам хэшкода. Если мой вопрос сильно им противоречит, то я его переработаю. PPS Если эта идея всё ещё кому-то интересна, то я могу завести бложик на blogspot'e, где можно писать "новости" 1-2 раза в месяц.


Ответ

Вообще, после составления 5-6 графов, которые изображали бы архитектуру программы, у меня получилась вот такая штука.
И, честно говоря, она мне не очень нравится. Вот почему: все остальные модули зависят от ядра и если вдруг придётся его изменить, то изменения могут затронуть всю программу, а это явный признак плохой архитектуры.
И потом: данный рисунок не отражает степень связанности разных модулей с ядром. Пожалуй, наиболее сильно с ядром связаны модули Input, GUI, Save/Load и AI. Чуть слабее связан 3D Render, так как он просто отрисовывает картинку, ну а генератор карт вообще почти никак с ядром не связан, от него требуется только генерировать карту по сигналу.
Возвращаясь к вопросу о том, как грамотно составить Roadmap, теперь я понимаю, что нужно начать с ядра, как бы это не было банально. После чего перейти к модулям ввода и рендера. Дальше GUI, Save/Load, генератор карт и AI.
Пока как-то так.
Два месяца спустя
За прошедшие два месяца в моей жизни поменялось довольно много: я переехал в другой город и нашёл работу. Мысли о Mad Lab не покидали мою голову и тут, ВНЕЗАПНО, случился невероятный прорыв: в сети я наткнулся на замечательный игровой движок, именуемый Panda3D. Написан он на С++, но все функции можно вызывать из питона. Открыт для любого использования и включает в себя очень обширный набор инструментов, таких как работа с GUI, физикой и звуком!
Знакомиться с движком я только начал, но уже написал по инструкции своеобразный helloworld, в котором по небольшой сценке туда-сюда бродит панда, а камера вращается вокруг центра сцены. Всё это счастье укладывается в 45 строк кода на питоне, что не может не радовать. В голове уже начинает складываться пресловутый roadmap: "так сейчас нам нужно реализовать перемещение человечка по карте при помощи point-and-click, затем добавить врагов,NPC, шмот и всё заверте..."
А вот, собственно, и код:
from math import pi, sin, cos
from panda3d.core import Point3
from direct.task import Task from direct.actor.Actor import Actor from direct.showbase.ShowBase import ShowBase from direct.interval.IntervalGlobal import Sequence
class MyApp( ShowBase ): def __init__( self ): ShowBase.__init__( self )
self.disableMouse( )
self.environ = self.loader.loadModel( "models/environment" ) self.environ.reparentTo( self.render ) self.environ.setScale( 0.25, 0.25, 0.25 ) self.environ.setPos( -8, 42, 0 )
self.taskMgr.add( self.spinCameraTask, "SpinCameraTask" )
self.pandaActor = Actor( "models/panda-model", { "walk": "models/panda-walk4" } ) self.pandaActor.setScale( 0.005, 0.005, 0.005 ) self.pandaActor.reparentTo( self.render ) self.pandaActor.loop( "walk" )
pandaPosInterval1 = self.pandaActor.posInterval( 13, Point3( 0, -10, 0 ), startPos=Point3( 0, 10, 0 ) ) pandaPosInterval2 = self.pandaActor.posInterval( 13, Point3( 0, 10, 0 ), startPos=Point3( 0, -10, 0 ) )
pandaHprInterval1 = self.pandaActor.hprInterval( 3, Point3( 180, 0, 0 ), startHpr=Point3( 0, 0, 0) ) pandaHprInterval2 = self.pandaActor.hprInterval( 3, Point3( 0, 0, 0 ), startHpr=Point3( 180, 0, 0 ) )
self.pandaPace = Sequence( pandaPosInterval1, pandaHprInterval1, pandaPosInterval2, pandaHprInterval2, name="pandaPace") self.pandaPace.loop( )
def spinCameraTask( self, task ): angleDegrees = task.time * 6.0 angleRadians = angleDegrees * ( pi / 180.0 ) self.camera.setPos( 20 * sin( angleRadians ), -20.0 * cos( angleRadians ), 3 ) self.camera.setHpr( angleDegrees, 0, 0 ) return Task.cont
app = MyApp( ) app.run( )

Расширение для Visual Studio 2013: добавление кнопки в стандартное (контекстное) меню

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


Ответ

Ответ частично найден.
Надо просто создать стандартный проект - Visual Stutio Package. Выбрать Menu Command в качестве интерфейса в диалоговом окне при создании (первый вариант). Потом открыть файл .vsct и поменять родителя у группы:

Именно

Привяжет группу (с кнопкой) в меню, появляющееся при нажатии правой кнопкой в коде. тут важен id этого меню IDM_VS_CTXT_CODEWIN и стандартный guid guidSHLMainMenu. Можно посмотреть другие id и guid (лучше в VS - там с описанием) https://msdn.microsoft.com/ru-ru/library/vstudio/microsoft.visualstudio.shell.vsmenus_members(v=vs.100).aspx#mainBody
В обработчике можно написать что угодно и оно заработает
private void MenuItemCallback(object sender, EventArgs e) { MessageBox.Show("Уже что-то заработало!!!"); }

Управление стилями по умолчанию Summernote

Редактор Summernote по умолчанию включает режим "жирный" при фокусе на пустом окне редактора:

Что нужно сделать, чтобы отменить это поведение? Я не нашел явных настроек конфигурации, очевидно, он эту информацию откуда-то берет, но даже подмена document.queryCommandState не помогла. Система целиком висит на опенкарте, который сам по себе кхм-кхм как в части исполнения, так и документации, поэтому проблему сложно оттрейсить.
Summernote вызывается самым обычным способом:
$('textarea').summernote();


Ответ

Проблема оказалась тривиальной: у Summernote нет конфигурации включенных стилей по умолчанию (во всяком случае, я ничего такого не обнаружил), однако он подтягивает стили того элемента, на котором установлен курсор. Textarea (равно как и заменивший ее элемент summernote) лежала внутри label, к которому применялось правило font-weight: bold, на который и срабатывал Summernote. Простого
label * { font-weight: normal; }
хватило для решения проблемы.
Спасибо @ReinRaus за подталкивание меня в нужном направлении.

Как получить данные о видео Youtube аяксом после обновления API?

Пытался так:
https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=_bWeZuPUZRA‌​&key=AIzaSyCRDrvUf4V4IjumKoo5woctjWw_scJoJ74
Дает Bad Requst.


Ответ

Ваш url, с подстановкой другого ключа и вырезания невидимого юникодового мусора между ZRA и &key , выдает вполне нормальный результат:
https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=_bWeZuPUZRA&key=AIzaSyDJz3xKLywmis4k-GxCigLzvhBuc8BHa6I
{ "kind": "youtube#videoListResponse", "etag": "\"NO6QTeg0-3ShswIeqLchQ_mzWJs/u_ANb0gbXz0_uU5DHO4CxFlAV3E\"", "pageInfo": { "totalResults": 1, "resultsPerPage": 1 }, "items": [ { "kind": "youtube#video", "etag": "\"NO6QTeg0-3ShswIeqLchQ_mzWJs/b1x_YkeRGElE_k5_2EH6NJ6i148\"", "id": "_bWeZuPUZRA", "contentDetails": { "duration": "PT3M53S", "dimension": "2d", "definition": "sd", "caption": "false", "licensedContent": false } } ] }
мусор можно заметить если сначала вставить вашу ссылку в адресную строку, потом скопировать, и еще раз вставить - появляется id=_bWeZuPUZRA%E2%80%8C%E2%80%8B&key вместо id=_bWeZuPUZRA&key. Или по задержке курсора при переходе между символами A&. Проверяйте код, которым вы строите ссылку - в нем явно глюк.

Python3: Символы \x90 \x8D \x8F \x8D \x81 в тексте с кодировкой cp1252

При получении данных из таблицы MySQL (используя Connector/Python) текстовые строки имеют вид:
Калашникова Елена Сергеевна
и
Тищенко Ð\x90ндрей ВаÑ\x81ильевич
Декодер показывает, что это кодировка CP1252, поэтому в Python3 мы можем обработать их str.encode('cp1252').decode('utf8'), однако если этот метод работает для первой строки, то не работает для второй.
UnicodeEncodeError: 'charmap' codec can't encode character '\x90' in position 16: character maps to undefined
Проблема - в специальных символах \x90, \x9D, \x8F, \x8D, \x81. В описании Windows-1252 указано, что эти коды относятся к C1 control codes.
Каким образом можно декодировать эти строки в Python3, не изменяя базу MySQL? Или где ещё может быть источник проблемы?


Ответ

Нашёл ответ!) При импорте базы данных нужно использовать кодировку latin1 Тогда в Python3 для декодирования таких строк необходимо выполнить str.encode('latin1').decode('utf8') На решение натолкнула эта статья - Solving Latin1 and UTF8 errors for good in Ruby
Latin1 is a character encoding used by MySQL. People mistakenly think it is equivalent to ISO-8859-1 but it isn’t, it’s actually CP-1252 (also known as Windows-1252). CP-1252 is a superset of ISO-8859-1 with some additional characters (recently including the € symbol).
Соответcnвенно, я нашёл в списке поддерживаемых кодировок latin1, и использовал её при подключении к MySQL с помощью Connector/Python.

Как работать с ресурсами проекта?

Как в Intellij IDEA 14 работать с ресурсами приложения?
В моём случае это картинки для приложения.
Я создал папку с ресурсами в проекте, а как использовать их — не знаю.


Ответ

вне зависимости от используемого ide — работать с ресурсами вы будете на языке java
мой вольный перевод справки по ссылке:
Retrieving Resources — получение ресурсов
используйте метод getResource для чтения ресурсов из jar-файла
к примеру, этот код получает изображения из *jar-файла:
// получить текущий classloader ClassLoader cl = this.getClass().getClassLoader(); // создать изображения Icon saveIcon = new ImageIcon(cl.getResource("images/save.gif")); Icon cutIcon = new ImageIcon(cl.getResource("images/cut.gif"));
в примере подразумевается, что следующие файлы присутствуют в jar-файле
images/save.gif images/cut.gif

а вот как именно добавить каталоги/файлы с ресурсами в jar-файл — зависит от используемого ide. подробности должны быть описаны в соответствующей справке.

Как поставить вызовы асинхронной ф-ии в очередь, и не чаще 3 в секунду?

Асинхронная ф-я может вызываться из разных мест большого кода, с передачей коллбэка для возврата результата.
Есть ограничение: нельзя вызывать эту ф-ю чаще 3 раз в секунду.
Не могу сообразить, как проще сделать очередь задач.
Сделал два буфера: массив времён последних трёх вызовов times и массив объектов заданий tasks, где каждый содержит параметры вызова и callback.
Перед выполнением очередного вызова необходимая задержка определяется как неотрицательная разность текущего времени c моментом times[0]. После выполнения вызова, текущее время заносится в хвост: times.push(ts); times = times.slice(-3)
Ф-я вызывается с необх. таймаутом либо по поступлении первого объекта в пустую очередь, либо по завершении очередного задания из очереди.
Есть ли в каком-нибудь JS фреймворке похожий функционал, чтобы списать, и правильно ли я подхожу к задаче перестроения асинхронных вызовов в последовательность?
App.timeSpan = 1000; App.times = []; App.tasks = [];
// точка входа App.api = function( method, params, callback) { var toWait, dt, ts = (new Date()).getTime();
this.tasks.push({ method: method, params: params, callback: callback });
if( this.tasks.length == 1) { if( this.times.length > 2) { dt = ts - this.times[0]; toWait = ( dt < this.timeSpan) ? this.timeSpan - dt : 0; } else { toWait = 0; } window.setTimeout(this.execute.bind(this), toWait); } }
App.execute = function(){ if( this.tasks.length == 0) return;
// вызов ТОЙ ф-ии VK.api( this.tasks[0].method, this.tasks[0].params, this.executed.bind(this)); }
App.executed = function(r){ var ts = (new Date()).getTime() // timestamp in seconds ,dt ,toWait ;
this.times.push( ts); this.times = this.times.slice(-3);
if( this.times.length > 2) { dt = ts - this.times[0]; toWait = ( dt < this.timeSpan) ? this.timeSpan - dt : 0; } else { toWait = 0; }
if( this.tasks.length > 1) { window.setTimeout(this.execute.bind(this), toWait); }
this.tasks.shift().callback.call(this, r); }


Ответ

По-моему, радикально упростить код не получится. Получилось лишь сделать его чуть более "красивым":
//for tests function mock(m, p, callback) { var time = Math.floor(Math.random() * 300); setTimeout(function() { callback(m + ' ' + p); }, time); } //for tests function callbackMock(r) { console.log("Callbacked: " + r); } function App() { } App.callsPerTimeSpan = 3; App.timeSpan = 1000; App.times = []; App.tasks = []; App.handling = false; App.taskHandler = mock; //mock - for tests App.api = function(method, params, callback) { this.tasks.push({ method: method, params: params, callback: callback }); if (!this.handling) { this.handling = true; this.execute(); } }; App.execute = function() { if (this.tasks.length == 0) { this.handling = false; return; } var toWait = 0; if (this.times.length == this.callsPerTimeSpan) { var ts = this.getCurrentTime(); var dt = ts - this.times[0]; toWait = (dt < this.timeSpan) ? this.timeSpan - dt : 0; } var task = this.tasks[0]; var that = this; window.setTimeout(function() { that.taskHandler(task.method, task.params, that.executed.bind(that)); }, toWait); }; App.executed = function(r) { var ts = this.getCurrentTime(); this.times.push(ts); this.times = this.times.slice(-this.callsPerTimeSpan); var task = this.tasks.shift(); var that = this; setTimeout(function() { task.callback.call(that, r); }, 0); //async call this.execute(); }; App.getCurrentTime = function() { return (new Date()).getTime(); }; //for tests for (var i = 0; i < 10; i++) { App.api(String.fromCharCode(i + 65), i, callbackMock); }
"Красота" в первую очередь включает в себя отсутствующий дублированный код расчета времени и исполнения следующей задачи.
Пример с выводом отладочной информации в fiddle

Решение задач. Количество страниц памяти

Здравствуйте, уважаемые коллеги!
Вопрос, может быть, достаточно тривиальный. Есть память EEPROM, размер страницы в ней - 16 байт (см. рисунок). Можно начать запись с любого байта, но за один сеанс записи нельзя вылезти в соседнюю страницу. В связи с этим требуется определить количество страниц памяти, которые подлежат записи. При этом известен номер ячейки, с которой начинается запись, и общее количество записываемых байт. Следует учесть и ту ситуацию, что одно и то же количество информации может занимать разное количество страниц в зависимости от начального адреса (см. рисунок).
Итак, коллеги, можете ли вы предложить маленькую красивую формулу, по которой можно вычислить количество страниц? Мне бы не хотелось использовать циклические алгоритмы. Язык - Си.
Не буду выкладывать свои наработки, скажу только, что моё решение не учитывает последней обозначенной проблемы, результат получается как в нижней части рисунка.


Ответ

static inline n_pages (unsigned int addr, unsigned int len) { return len ? ((addr + len - 1) >> 4) - (addr >> 4) + 1 : 0; }
вроде так?
(первое выражение -- номер страницы с адресом последнего байта данных (addr + len - 1 это адрес последнего байта) , второе -- номер страницы с началом данных, соответственно их разница + 1 -- это количество страниц)

Сломанная кодировка при выводе в Python 2.7

Имеется сценарий, когда я его выполняю, вместо русских символов появляется:
X Получено: None | Ожидалось: '\xd0\xad\xd1\x82\xd0\xbe\xd1\x82 \xd1\x84\xd0\xb8\xd0\xbb\xd1\x8c\xd0\xbc \xd1\x85\xd0\xbe\xd1\x80\xd0\xbe\xd1\x88'.
Гугл предложил решение проблемы путем добавления в начало файла строк:
# -*- coding: utf-8 -*- from __future__ import unicode_literals
Но в таком случае появляется вывод:
u'\u042d\u0442\u043e\u0442 \u0444\u0438\u043b\u044c\u043c \u0445\u043e\u0440\u043e\u0448'
Т.е добавляется префикс "u",но на итог это почти никак не влияет. Пробовал менять кодировку файла. Не помогает. Понимаю, вопрос глупый и решается, скорей всего, элементарно.
Код:
def main(): test(donuts(4), 'Количество : 4') test(donuts(9), 'Количество : 9')
Код функции:
def donuts(count):+ if count<10: rezult = 'Количество: {}'.format(count) else: rezult = 'Количество: много'
return rezult
Функция test
def test(got, expected): if got == expected: prefix = ' OK ' else: prefix = ' X ' print('%s Получено: %s | Ожидалось: %s' % (prefix, repr(got), repr(expected)))
Проблема одна и та же в Ubuntu и Windows. В данный момент пытаюсь бороться в Windows XP


Ответ

Кракозябы вы видите потому что используете байты, а не Unicode для текста, что ведёт к выводу текста в неправильной кодировке (текст закодированный в utf-8 выводится в Виндовой кодировке):
>>> u'Получено'.encode('utf-8').decode('cp1251') 'Получено'
Не используйте байты, используйте Unicode для представления текста
from __future__ import unicode_literals заставляет 'abc' строковые константы создавать Unicode строки на Питоне 2 (их поведение из Питона 3). В противном случае, используйте u'' префикс, чтобы текст в виде констант в коде задавать.

u'\u042d\u0442\u043e' появляется потому что вы вызываете repr() функцию, которая возвращает текстовое представление объекта. Задача repr() функции получить однозначное представление объекта, например, для отладки, тестов. Идеально, когда eval(repr(obj)) == obj
В общем случае, чтобы напечатать Юникодную строчку в Питоне, уберите repr()
>>> print(u'\u042d\u0442\u043e') Это
Если печатаемые символы поддерживаются в текущем окружении (кодировка консоли на Винде, правильная локаль в *nix), то этого достаточно. Если вывод текста перенаправлен в Питоне 2 или хочется произвольные символы печатать на Windows, даже те которые не поддерживаются OEM codepage такой как cp866, то настройте PYTHONIOENCODING, если вывод перенаправлен в файл, pipe или установите win-unicode-console пакет, чтобы в Windows консоль показывать любые (BMP) символы (если правильный шрифт настроен)

Отображение View по-разному в зависимости от роли пользователя WPF

Всем привет. Суть проблемы. Есть пользователи с разными ролями, например: админ, модератор, юзер.
Я хочу иметь возможность ограничить то, что пользователи могут видеть на основе их роли. Т.е. на одной и той же View могут быть скрыты, заблокированы или же вовсе появится новые элементы управления, в зависимости от роли текущего пользователя.
Как такую задачу решить максимально гибко и правильно в контексте паттерна MVVM?


Ответ

На мой взгляд, имеет смысл создать иерархию моделей представления:
abstract class BaseRoleVm { ... } class UserRoleVm : BaseRoleVm { ... } class ModeratorRoleVm : BaseRoleVm { ... } class AdminRoleVm : BaseRoleVm { ... }
Затем для каждой неабстрактной ViewModel создать по представлению (для переиспользования кода в представлении можно использовать стили, контролы, словари ресурсов).
В основном окне нужно будет завести свойство типа BaseRoleVm, которому будет присваиваться нужный наследник:
class MainVm { public BaseRoleVm Role { get; set; }
void UseAdminRole() { Role = new AdminRoleVm(); } ... }
Такой подход позволит легко вносить изменения в представления/модели представления ролей, обеспечит их независимость друг от друга, предоставить возможность добавлять/удалять роли без вмешательства в логику работы основного окна.
UPD
Вот небольшой пример на основе словарей ресурсов:
Модели представления (лежат в папке ViewModels):
public abstract class BaseRoleVm : BaseVm { public abstract string Name { get; } }
public class AdminRoleVm : BaseRoleVm { public override string Name { get { return "Админ"; } } }
public class UserRoleVm : BaseRoleVm { public override string Name { get { return "Юзер"; } } }
public class MainVm : BaseVm { public BaseRoleVm SelectedRole { get { return _selectedRole; } set { SetField(ref _selectedRole, value); } } private BaseRoleVm _selectedRole;
public BaseRoleVm[] Roles { get; }
public MainVm() { // формировать список или создавать нужную модель представления можно через MEF или Reflection. // тогда вы не будете зависеть от перечня ролей Roles = new BaseRoleVm[] { new AdminRoleVm(), new UserRoleVm(), };
SelectedRole = Roles.FirstOrDefault(); } }
Представления (лежат в папке Views)
// содержимое файла "AdminRole.xaml" (тип Resource Dictionary)
// содержимое файла "UserRole.xaml" (тип Resource Dictionary)
// главное окно // содержимое файла "Main.xaml" (тип Window)
И простейший селектор шаблонов:
public class ViewModelTemplateSelector : DataTemplateSelector { public override DataTemplate SelectTemplate(object item, DependencyObject container) { if (item == null) { return null; }
var itemType = item.GetType();
var resourceDictonary = new ResourceDictionary { Source = new Uri(string.Format( "pack://application:,,,/{0};component/Views/{1}.xaml", itemType.Assembly.FullName, itemType.Name.Replace("Vm", string.Empty))) }; return resourceDictonary .Values .OfType() .SingleOrDefault(_ => ReferenceEquals(_.DataType, itemType)); } }
На уровне представления роли никак не связаны между собой. ContentControl использует написанный ViewModelTemplateSelector для поисков нужных шаблонов. Происходит примерно следующее:
У ContentControl меняется Content (сработало PropertyChanged) ContentControl вызывает у ViewModelTemplateSelector метод SelectTemplate и передает туда Content в качестве item ViewModelTemplateSelector находит файл с представлением на основе имени типа и достает оттуда DataTemplate ContentControl отображает Content с использованием найденного DataTemplate

Алгоритм поиска количества известных слагаемых

Есть круг разделен на 16 секторов.Каждый сектор разделен на 10 сегментов.
В секторе 11 сегменте 7 сидит лягушка, которая прыгает: 1) прямо на 3, или на 2 прямо и 1 в влево, или 2 прямо и 1 вправо, или на 1 прямо и 2 в влево, или на 1 прямо и 2 вправо. Она должна допригнуты до 9 сектора 10 сегмента.
Есть две елки на круге на которые она не может прыгнуть.1 елка - сектор 14 сегмент 9, 2 - сектор 5 сегмент 8.
Надо найти минимальное количество прыжков, за которые лягушка достанется в сектор 9 сегмента 10
Лягушка может прыгать только по часовой стрелке. Через центр не может.
За круг также не может выпрыгнуть. Длина прыжка измеряется в секторах и сегментах.
Если прижок 3.0, это значит, что она прыгает на 3 сектора в перед (прямо), если 2.1 - это значит, что на 2 сектора вперед и сместилась на 1 позицию влево к краю круга (была - 11.7 стала 13.9) .если 2. -1 то 13.6(сместилась к центру)
У меня есть пять пар чисел {3,0},{2,1},{2,-1},{1,2},{1,-2}.
Эти пары чисел можно добавлять, например, так: 3, 0 + 2,1 = 5,1 или так: 3.0 + 3.0 + 2.1 + 1,2 = 9.3 или так: 3.0 + 2.1 + 1.2 + 2.(- 1) = 8.2
Надо составить алгоритм, чтобы найти минимальное количество этих пар чисел (и сами пары), сумма которых будет 14.3.


Ответ

Упаковка рюкзака, правда в странной форме.
Заводите двоичное число, в котором количество битов соответствует количеству ваших пар.
Затем, пишите функцию, которая будет смотреть на биты этого числа и набирать из пар результат. Если дошли до нужного результата - ура, если превысили или так и не достигли - увеличиваем наше двоичное число и повторяем еще раз.
Сложность брутфорса 2^N, так что если у вас не много пар - дерзайте.

Какой алгоритм регрессии выбрать для шумных (разбросанных) данных?

Я хочу построить регрессию с несколькими переменными (multiple features). В моих данных у меня n = 23 переменных и m = 13000 тренировочных примеров. Вот график моих тренировочных данных (площадь квартиры vs цена):
Здесь на графике отображены 13000 тренировочных данных. Как вы можете видеть, это достаточно шумные данные. Мой вопрос: какой алгоритм регрессии больше подходит и обоснован для использования в моем случае. Имею ввиду логично ли использовать простую линейную регрессию или лучше использовать какой-либо нелинейный алгоритм регрессии.
Для наглядности приведу примеры. Вот отвлеченный пример линейной регрессии:
А также отвлеченный пример нелинейной регрессии:
А вот примеры с гипотетическими линиями регрессии для моих данных:
Насколько я понимаю, примитивная линейная регрессия для моих данных произведет большую суммарную погрешность (error cost), так как эти данные шумные и разбросанные. С другой стороны, здесь также не прослеживается какой-либо отчетливой нелинейной зависимости (например синусоидальной). Какой алгоритм регрессии более рационально использовать в моем случае (цены на квартиры) для того чтобы получить более точное прогнозирование цен. И почему этот алгоритм (линейный или нелинейный) более рационален?
Дополнение: Вот так выглядит мой график линейной зависимости цены от всех 23 параметров:
Я не знаю, как бы выглядела НЕлинейная зависимость в таком случае. И была бы она более рациональна чем линейная.


Ответ

Для сравнения статистических моделей обычно используют информационные критерии, например, информационный критерий Акаике. Если вы пишете на R, то посмотрите функцию stepAIC - она позволяет упростить линейную модель, выкидывая из нее предикторы по одному, в порядке возрастания значимости для модели.

Разница между override и virtual [дубликат]

На данный вопрос уже ответили: virtual и override 4 ответа Многие примеры на сайтах показывают использование virtual в объявлении класса, но в дочерних нет никакого override. На msdn.microsoft.com же наоборот пихают virtual также и дочернему классу, + override.
Хотелось бы узнать, вот если я вызываю из базового класса метод
void Base::Method() {
},
Который не переопределяется, а в этом методе используются методы, которые переопределены в дочерних классах - они будут переопределены автоматически или только в случае (Son *) (ptr)->Method2()? И как будет выглядеть ситуация в обоих случаях если virtual void Base::Method2() = 0;?

Вопрос из ответа участника @ Artem


Ответ

virtual указывает, что дочерний класс может переопределить метод. Это совсем не означает, что в дочернем классе будет override. Он может быть.
На msdn.microsoft.com же наоборот пихают virtual также и дочернему классу, + override
К дочернем классам добавляют virtual к методу, чтобы указать, что его потомки так же могут переопределить метод.
Про вторую часть вопроса:
class Base{
void Method() { bar(); } virtual void bar(); { }
};
class A : public Base {
virtual void bar() { }
};
class B : public A {
virtual void bar() { // do something }
};
Сделав так:
A* myObject = new B(); myObject->Method();
Вызовется метод из класса B
Собственно, возвращаясь к virtual. Я мог в классах A и B не объявлять методы как virtual, всё равно сработает метод из B
Точно так же, независимо от того, напишу я так virtual void bar() в B классе или так virtual void bar() override, тоже ничего не изменится. Что с override, что без него метод дочернего класса перекроет метод базового. Просто написав override мы явно это указываем. Очень полезно, если вы с кодом работаете не один.

Как сделать разноцветный background с повторением?

Нужно расположить 4 цвета последовательно. Каждый цветовой блок 100px. Если ширина экрана больше 400px, то повторять цвета. Если меньше, то убирать цвета. Цвета добавлять и удалять нужно по мере увеличения и уменьшения экрана. То есть, на 360px будет 3 полноценных цвета и 60px четвёртого цвета.
Допустим для экрана в 400px будет так:

Для 600px будет так:

Как это сделать на чистом CSS (без картинок)?


Ответ

Очень интересный вопрос, нашел следующее решение:
.block, .second-block { width: 400px; height: 100px; margin-bottom: 20px; background-image: repeating-linear-gradient(90deg, #FF0000 0px, #FF0000 100px, #FF9000 100px, #FF9000 200px, #FCFF00 200px, #FCFF00 300px, #12FF00 300px, #12FF00 400px); } .second-block { width: 600px; }


Инфо: MDN

Расположение кода программы в памяти компьютера

Прочитал, что программы скомпилированные на VS имеют базовый адрес в памяти 0x00400000. Стало интересно как в памяти буду располагаться 2 программы. Написал, запустил и обе пишут адрес 0x00400000, но как? почему две разные программы располагаются по 1 адресу? разве такое возможно?


Ответ

Это виртуальная память, которая у каждого процесса своя. В итоге, два процесса могут быть загружены по виртуальному адресу 0x400000 и не мешать друг-другу. Есть физическая память, допустим 4 гига - от 0x00000000 до 0xFFFFFFFF. Ядро операционной системы выделяет, например, физический кусок 0x70000000 - 0x80000000 для процесса. И изнутри процесс видит эту память как 0x400000 - 0x10400000. Для второго процесса выделается 0x8000000-0x90000000, а изнутри он видит те же адреса, что и первый.

Как WCF служба может узнать, что клиент отсоединился?

Когда клиент хочет отредактировать запись, то шлется запрос WCF службе, что бы она в свою очередь обновила статус этой записи в БД на "редактируется", что бы другие клиенты с ней ничего не могли сделать на время редактирования.
Так вот, если неожиданно клиент отрубился(пропал интернет и все такое), как WCF служба может узнать, что клиента, который производил редактирование, больше нет и нужно вернуть статус на доступно?
А что если деструктор на стороне службы сделать? По идее если клиент не отвечает долгое время, то его экземпляр умирает и тут в дело приходит мой деструктор , который разблокирует записи, если такие есть


Ответ

Для этих целей лучше использовать класс IChannelInitializer - класс, предназначенный для детектирования подключения клиентов, в котором вы можете установить события на закрытие канала(чтобы не писать их в каждой реализации метода службы)
Например, создадим класс ClientTrackerChannelInitializer, который наследует интерфейс IChannelInitializer
class ClientTrackerChannelInitializer : IChannelInitializer { internal static int ConnectedClientCount = 0;
// метод, который определяет, что клиент подсоединился, создался новый канал public void Initialize(IClientChannel channel) { ConnectedClientCount++; Console.WriteLine("Client {0} initialized", channel.SessionId); channel.Closed += ClientDisconnected; channel.Faulted += ClientDisconnected; } // событие, на которое подписались при создании канала static void ClientDisconnected(object sender, EventArgs e) { Console.WriteLine("Client {0} disconnected", ((IClientChannel)sender).SessionId); ConnectedClientCount--; } }
У него два метода
Initialize - метод, который срабатывает при подключении нового клиента ClientDisconnected - метод, обработчик события закрытия канала(или ошибки в канале)
Теперь необходимо подключить класс ClientTrackerChannelInitializer на стороне сервера. Для этого создадим класс, реализующий интерфейс IEndpointBehavior, класс, который позволяет расширять поведение конечной точки службы
class ClientTrackerEndpointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // подключаем наш класс endpointDispatcher.ChannelDispatcher.ChannelInitializers.Add(new ClientTrackerChannelInitializer()); }
public void Validate(ServiceEndpoint endpoint) { } }
В методе ApplyDispatchBehavior мы подключили класс ClientTrackerChannelInitializer. Все классы созданы. Теперь можно публиковать службу. Предположим, что у нас есть сервис IStackCalculator. Класс StackCalculator реализует данный сервис
[ServiceContract(SessionMode = SessionMode.Required)] public interface IStackCalculator { [OperationContract] void Enter(double value); [OperationContract] double Add(); [OperationContract] double Subtract(); [OperationContract] double Multiply(); [OperationContract] double Divide(); }
Теперь при публикации хоста сервиса подключим наше расширение конечной точки.
string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(StackCalculator), new Uri(baseAddress)); WSHttpBinding binding = new WSHttpBinding(SecurityMode.None); binding.ReliableSession.Enabled = true; ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IStackCalculator), binding, ""); // подключаем расширение конечной точки службы endpoint.Behaviors.Add(new ClientTrackerEndpointBehavior()); host.Open(); Console.WriteLine("Host opened");
Теперь при соединении/отключении клиентов будут срабатывать события Initialize и ClientDisconnected нашего класса ClientTrackerChannelInitialize. Нет необходимости прописывать подписывание на событие закрытия канала в самой реализации метода.
Механизм создания классов-расширения служб, реализующих интерфейс IEndpointBehaviour, можно применять для многих целей. По своему опыту применял для подсчета количества вызовов функций сервиса, для замера скорости выполнения функции сервиса, для аутентификации пользователя... Посмотрите Примеры использования IEndpointBehavior.
Более подробно о примере, который мы рассмотрели, можно прочитать здесь Пример кода
P.S. Если необходимо подключить кастомный IEndpointBehavior через файл config, напишите, добавлю в ответ.
Edit: Чтобы подключить наш класс-расширение конечной точки ClientTrackerEndpointBehavior, мы должны сделать следующее
Зарегистрировать наше поведение в < system.servicemodel >/< extensions >/< behaviorExtensions > указав имя расширения и тип класса, реализующего расширение(тип класса ClientTrackerEndpointBehavior)
< extensions > < behaviorExtensions> < add name="clientTracker" type="ClientTrackerEndpointBehavior, Client.ExtensionsDLL /> < /behaviorExtensions> Добавить расширение конечной точки в элементе < behaviors >. Здесь мы в качестве расширения указываем элемент-расширение clientTracker, которое определили на предыдущем шаге
< behaviors> < endpointBehaviors> < behavior name="ServiceBehaviorExtension"> < clientTracker /> < /behavior> < /endpointBehaviors> < /behaviors> Теперь при определении конечной точки сервиса мы можем подключить поведение через атрибут behaviourConfiguration, указав имя расширения, указанного выше
< services> < service name="StackCalculator" > < endpoint name="endpoint1" address="" binding="basicHttpBinding" bindingName="binding" contract="IStackCalculator" behaviorConfiguration="ServiceBehaviorExtension" / > ... < /services >
Пример можно посмотреть например отсюда

Разные отступы Floating Action Button(fab) в разных версиях Android

Здраствуйте! Есть така проблема: нужно разместить 2 Floating Action Buttons c привязкой к правому нижнему углу с задаваемым margin между ними. Наглядный пример - Google Maps. Гугл привел меня к такой ссылке, где описано именно то, что мне нужно. Но делая по примеру, я обнаружил, что на своем телефоне(скрин 1, android 4.2) и на другом (скрин 2, android 5.0.2) есть разбежности. Думаю, здесь нужно баловаться с dimens.xml and styles.xml для каждой версии API(а различия между x<20 & x >= 20).
Приведу код:



P.S. Как видите, разница большая. Интересует как указать margin между кнопками по вертикали? Задавал их, но не работает(наверное через anchor).


Ответ

Solution
Взяв за основу ответ @shwarz-andrei, я начал экспериментировать со своей задачей. Итого, у меня получилось(хотя тестировал только на двух вышесказанных девайсах).
И так, начнём. Для начала я обьявил константы в values/dimens.xml:
16dp 16dp 16dp 32dp 4dp
... в values-v21/dimens.xml соотвествено:
16dp 16dp 8dp 64dp 8dp
Код разметки:


android:src="@android:drawable/arrow_up_float" app:layout_anchor="@id/divider" app:layout_anchorGravity="right|end" android:layout_marginBottom="@dimen/fab_margin_bottom" android:layout_marginRight="@dimen/fab_margin_extra_right"/>
Результат: всё заработало так, как мне нужно было, а именно - одинаковое отображение двух FAB на разных версиях Android(в моём случае - Android 4.2 и 5.0.1).

Преобразование Map в список пар с использованием Stream

Имеем:
Map> map = fillMap();
Необходимо максимально элегантно преобразовать его в List> используя Stream API (Pair из apache lang3).
Например, было:
[ a=[obj1,obj2,obj3], b=[obj4,obj5], c=[obj6,obj7] ]
хочу получить:
[ Pair{a,obj1}, Pair{a,obj2}, Pair{a,obj3}, Pair{b,obj4}, Pair{b,obj5}, Pair{c,obj6}, Pair{c,obj7} ]


Ответ

Можно с помощью операции flatMap (вместо Pair - AbstractMap.SimpleEntry), она принимает функцию, возвращающую Stream и встраивает элементы возвращенного потока в основной:
Map> map = new HashMap<>();
map.put( "a", Arrays.asList( "obj1", "obj2", "obj3" ) ); map.put( "b", Arrays.asList( "obj4", "obj5" ) ); map.put( "c", Arrays.asList( "obj6", "obj7" ) );
System.out.println( map );
List> list = map.entrySet().stream().flatMap( entry -> entry.getValue().stream() .map( listElement -> new AbstractMap.SimpleEntry<>( entry.getKey(), listElement ) ) ).collect( Collectors.toList() );
System.out.println( list );

Копирование файлов посредством ansible

Что-то не смог понять из примеров в документации - как же скопировать файл с удалённого сервера на локальный и наоборот? Вот есть модули copy и fetch, например. Вот я пишу в плейбуке:
- name: Copy file from remote host to local machine fetch: src=/tmp/somefile dest=/tmp/fetched(взято из доки)
Как указать ansible, с какого хоста я хочу скопировать файл? Ну и соответственно, при копировании на удалённый хост, где указывать этот удалённый хост?
P.S. Немного разобрался: Чтобы скопировать файл с удалённого хоста на локальный:
- hosts: localhost
vars_files: - config.yml
tasks: - include: ../share/dev.yml
- name: Get file from remote fetch: src="{{ remote_sources_path }}/test.txt" dest="backup" delegate_to: '{{ remote_host }}' tags: fetch
Файл test.txt будет лежать в каталоге ./backup/localhost/{{ remote_sources_path }}
Скопировать с локального на удалённый:
- name: Send file to remote copy: src="{{ local_sources_path }}/to_remote_test.txt" dest={{ remote_sources_path }} delegate_to: '{{ remote_host }}' tags: fetch
Подозреваю, что, если указать в начале - hosts: {{ remote_host }} вместо localhost, то delegate_to использовать не нужно.


Ответ

Ну зачем же так сложно? ))
Все эти задачи решаются и без мороки с Delegation и Local Actions, вам же самим потом сложнее будет разбираться в собственных плейбуках или читать чужие, написанные по-простому.
Смотрите.
В каждом плейбуке вы указываете на каких хостах запускать задачи - пишете hosts: '{{ target }}' (если хотите из командной строки управлять списком через --extra-vars "target=10.0.100.123") или hosts: dbservers (если фиксировать плейбук на группу хостов):
--- # This playbook for quick tests.
- name: quick tests hosts: '{{ target }}' become: true become_user: root roles: - role1 - role2 tasks: - name: install mc yum: name=mc state=latest - name: install wget yum: name=wget state=latest
Эти хосты - "удалённый" сервер, ну а "локальный" - это само собой хост, на котором находится ваш плейбук.
Теперь:
Для копирования файла с локального сервера на удалённый -- используете модуль copy Для копирования файла с удалённого сервера на локальный -- используете модуль fetch
И в copy и в fetch src - это откуда брать файл, а dest - это куда положить файл. Для copy src=local а dest=remote, для fetch -- наоборот src=remote, а dest=local.
Вот и всё. Вам кстати именно об этом говорили в комментарии к вашему последнему вопросу: "В целом Ansible подразумевает то, что ты уже на удаленном хосте", не надо нарезать лишние петли вокруг локалхоста.
PS Если нужно копировать с произвольного хоста на произвольный хост - то смотрите в сторону модуля syncronyze на основе rsync. Но вообще в 90% случаев вам понадобится копирование с локального хоста на удалённый и будете использовать copy или template.

Реализовать возможность панорамной съемки

Здравствуйте, стоит задача реализации возможности съёмки панорамного фото в Android приложении. Сейчас в приложении вызывается просто стандартная камера через intent. Необходимо сделать свою имплементацию с возможностью отслеживания дрожания при съемке и, при необходимости, сообщать об этом пользователю, и заставлять делать фото заново. Подскажите, в какую сторону копать, искал инфу, говорят что такая возможность есть только при условии наличия некоторых библиотек, которые устанавливаются вендором устройства, но, везде их названия разные. Может есть какие сторонние библиотеки? В идеале, реализовать, как на CyanogenMod 13 (ведётся камера из точки A в точку B, как при съемке видео) а на выходе получается ровная, хорошего качества фотография без артефактов склеивания.


Ответ

Очень интересная статья, может помочь в вашей ситуации.

Уведомления с сайта в WhatsApp

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


Ответ

Для отправки WhatsApp сообщений с сайта можно использовать Chat-API
Пример кода:
$username = "79876543210"; // номер $nickname = "MySite"; // ник $password = "***"; // пароль
$w = new WhatsProt($username, $nickname); $w->connect(); $w->loginWithPassword($password);
$target = '79870123456'; // получатель $message = 'Сообщение';
$w->sendMessage($target , $message);

Быстрая замена в тексте латинских символов на кириллические со сходным начертанием

Здравствуйте. Приходится иметь дело с русскими текстами, где присутствует нежелательное вкрапление символов английского алфавита.

Т. к. расположение вкраплений всегда разное и не подчинено каким-либо правилам, я не могу осуществлять поиск слов в тексте.
Решение — заменить латинские буквы ABEKMHOPCTXaeopcyx на соответствующие им кириллические АВЕКМНОРСТХаеорсух: A → А, B → В и т. д. Как в Sublime Text эффективнее всего произвести все 18 замен?
Этот вопрос частный случай более общего: как произвести множество замен в одном регулярном выражении? Спасибо.


Ответ

Плагин для ST2, выполняющий желаемое:
# -*- coding: utf-8 -*- import sublime, sublime_plugin
class MultipleReplace(sublime_plugin.TextCommand): def run(self, edit): target = "ABEKMHOPCTXaeopcyx" # en replacer = u"абекмнорстхАЕОРСУХ" # ru region = sublime.Region(0, self.view.size()) fullText = unicode( self.view.substr( region ) ) count = 0 for ch in range( len(target) ): count += fullText.count( target[ch] ) fullText = fullText.replace( target[ch], replacer[ch] ) self.view.replace( edit, region, fullText ) sublime.status_message( "Hidden chars replaced: " + str( count ) )
Плагин для ST3, выполняющий желаемое:
# -*- coding: utf-8 -*- import sublime, sublime_plugin
class MultipleReplace(sublime_plugin.TextCommand): def run(self, edit): target = "ABEKMHOPCTXaeopcyx" # en replacer = u"абекмнорстхАЕОРСУХ" # ru region = sublime.Region(0, self.view.size()) fullText = self.view.substr( region ) count = 0 for ch in range( len(target) ): count += fullText.count( target[ch] ) fullText = fullText.replace( target[ch], replacer[ch] ) self.view.replace( edit, region, fullText ) sublime.status_message( "Hidden chars replaced: " + str( count ) )
Вечером распишу подробнее, как устанавливать плагины в ST.

Здесь можно найти, как устанавливать плагин: https://habrahabr.ru/post/136529/

Параллельные состояния в QStateMachine

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


Ответ

Реализация параллельных состояний в Qt State Machine Framework в отличие от последовательных имеет одну существенную особенность. Но для полноты картины начнём именно с последовательных. Создадим машину и пару состояний.
QStateMachine *machine = new QStateMachine(); QState *state1 = new QState(machine); QState *state2 = new QState(machine);
Если для нашей машины предполагается, что она, при наличии определённых условий (скажем, при прохождении двух ранее объявленных состояний), должна завершить свою работу, то удобно добавить в неё состояние завершения работы.
QFinalState *final_state = new QFinalState(machine);
Использование QFinalState удобно тем, что при входе в него контекста выполнения этот тип состояния автоматически отправляет своему родителю сигнал finished(). Если родителем является объект QStateMachine, то машина будет остановлена, если же это объект QState, то уже тот в свою очередь отправит такой же сигнал вверх по цепочке. Эта особенность в полной мере используется и при параллельном подключении состояний, что будет рассмотрено ниже.
К слову сказать, во всех примерах Qt-справки машина имеет неопределённую длительность жизни, тогда как на практике часто встречаются задачи, которые, с одной стороны требуют асинхронного выполнения (например, отправка и обработка сетевых запросов), а с другой - возникают по мере необходимости. Нет никакого интереса содержать в памяти объект машины со всеми состояниями, если, скажем, надо что-то выполнять регулярно, но не постоянно.
Для таких случаев при объявлении машины можно просто подключить соответствующие сигналы и объект машины ликвидирует сам себя и все дочерние объекты, как только завершит работу.
connect(machine, &QStateMachine::stopped, machine, &QStateMachine::deleteLater); connect(machine, &QStateMachine::finished, machine, &QStateMachine::deleteLater);
Для переключения состояний обычно используют сигналы от внешних по отношению к машине объектов.
state1->addTransition(external_obj1, SIGNAL(customSignal()), state2); state2->addTransition(external_obj2, SIGNAL(customSignal()), final_state);
Однако этот вариант не подходит, если таковых объектов не имеется. В этом случае можно добавить в класс, в котором существует объект машины, собственный произвольный сигнал. Например:
class MyClass : public QObject { Q_OBJECT
signals: void customSignal();
public: MyObject(QObject *parent = Q_NULLPTR) : QObject(parent) {}
public slots: void run(); };
void MyClass::run() { QStateMachine *machine = new QStateMachine(this);
connect(machine, &QStateMachine::stopped , machine, &QStateMachine::deleteLater); connect(machine, &QStateMachine::finished , machine, &QStateMachine::deleteLater);
QState *state1 = new QState(machine); connect(state1, &QState::entered, [this]() { // Код при входе в первое состояние ... emit customSignal(); });
QState *state2 = new QState(machine); connect(state2, &QState::entered, [this]() { // Код при входе во второе состояние ... emit customSignal(); });
QFinalState *final_state = new QFinalState(machine); connect(final_state, &QFinalState::entered, [this]() { // Здесь, например, можно уведомить // пользователя о завершении работы. });
// Подключаем переходы. state1->addTransition(this, SIGNAL(customSignal()), state2); state2->addTransition(this, SIGNAL(customSignal()), final_state);
// Указываем точку входа и активируем машину. machine->setInitialState(state1); machine->start(); }
Однако такой подход приводит к проблемам при необходимости расширения типов реакции машины на каждое из состояний.
Предположим, что теперь требуется учитывать успешно или неуспешно завершились state1 и state2. Конечно, можно добавить ещё несколько сигналов в MyClass, но что за "ёрш" в итоге получится, если и количество состояний будет расти? Очевидно, что в данной ситуации требуется знать не только результат выполнения кода при выходе из каждого состояния, но и, собственно, что это было за состояние из списка имеющихся. Помочь может использование событий и переходов (transition) по событиям.
Для начала класс произвольного события stateevent.h
#include
class QState;
class StateEvent : public QEvent { public: enum Result { RESULT_NULL, RESULT_SUCCEED, RESULT_FAILED };
static const QEvent::Type &_event_type;
StateEvent(QState *state, Result result);
QState *state() const {return _state;}
StateEvent::Result result() const {return _result;}
private: QState *_state; Result _result; };
... и stateevent.cpp
#include "stateevent.h"
const QEvent::Type &StateEvent::_event_type = static_cast(QEvent::registerEventType());
StateEvent::StateEvent(QState *state, Result result) : QEvent(_event_type), _state(state), _result(result) {}
А теперь и класс перехода:
#include
#include "stateevent.h"
class StateTransition : public QAbstractTransition { Q_OBJECT
public: StateTransition(QState *source_state = Q_NULLPTR) : QAbstractTransition(source_state) , _result(StateEvent::RESULT_NULL) {}
StateEvent::Result eventResult() const {return _result;} void setEventResult(StateEvent::Result result) {_result = result;}
protected: virtual bool eventTest(QEvent *event) { if(event->type() == StateEvent::_event_type) { StateEvent *state_event = static_cast(event); return (state_event->state() == sourceState() && state_event->result() == _result); }
return false; }
virtual void onTransition(QEvent *event) {Q_UNUSED(event);}
private: StateEvent::Result _result; };
Метод eventTest(QEvent*) проверяет, соответствует ли объект состояния (QState) и результат выполнения (StateEvent::Result) объекту перехода (StateTransition). Если истина, то машина переключится в новое состояние, которое назначено при выполнении перечисленных условий.
Теперь код активации машины меняется на следующий (произвольные сигналы нам больше не нужны):
class MyClass : public QObject { Q_OBJECT
public: MyObject(QObject *parent = Q_NULLPTR) : QObject(parent) {}
public slots: void run(); };
void MyClass::run() { QStateMachine *machine = new QStateMachine(this);
connect(machine, &QStateMachine::stopped , machine, &QStateMachine::deleteLater); connect(machine, &QStateMachine::finished , machine, &QStateMachine::deleteLater);
QState *state1 = new QState(machine); connect(state1, &QState::entered, [this,state1]() { // Код при входе в первое состояние ... bool result = ... ;
if(result == true) { state1->machine() ->postEvent(new StateEvent(state1 , StateEvent::RESULT_SUCCEED)); } else { state1->machine() ->postEvent(new StateEvent(state1 , StateEvent::RESULT_FAILED)); } });
QState *state2 = new QState(machine); connect(state2, &QState::entered, [this,state2]() { // Код при входе во второе состояние ... bool result = ... ;
if(result == true) { state2->machine() ->postEvent(new StateEvent(state2 , StateEvent::RESULT_SUCCEED)); } else { state2->machine() ->postEvent(new StateEvent(state2 , StateEvent::RESULT_FAILED)); } });
QFinalState *final_state = new QFinalState(machine); connect(final_state, &QFinalState::entered, [this]() { // Здесь, например, можно уведомить // пользователя о завершении работы. });
// Подключаем переходы. { StateTransition *success_transition = new StateTransition(); success_transition->setEventResult(StateEvent::RESULT_SUCCEED); success_transition->setTargetState(state2);
StateTransition *fail_transition = new StateTransition(); fail_transition->setEventResult(StateEvent::RESULT_FAILED); fail_transition->setTargetState(final_state);
state1->addTransition(success_transition); state1->addTransition(fail_transition); }
{ StateTransition *success_transition = new StateTransition(); success_transition->setEventResult(StateEvent::RESULT_SUCCEED); success_transition->setTargetState(final_state);
StateTransition *fail_transition = new StateTransition(); fail_transition->setEventResult(StateEvent::RESULT_FAILED); fail_transition->setTargetState(final_state);
state2->addTransition(success_transition); state2->addTransition(fail_transition); }
// Указываем точку входа и активируем машину. machine->setInitialState(state1); machine->start(); }
С помощью продемонстрированного механизма можно свободно "плодить" различные типы результата работы для состояний, что в свою очередь позволит конечному автомату переключать эти самые состояния в той последовательности и по тем правилам, что заранее были для него определены.
При отправке события о завершении работы каждого из состояний, например:
state1->machine() ->postEvent(new StateEvent(state1 , StateEvent::RESULT_SUCCEED));
... в StateEvent вносится и сохраняется указатель на объект состояния, из которого должен быть осуществлён переход. При этом метод QStateMachine::postEvent(QEvent*) отправит вновь созданное событие всем объектам перехода, которые были зарегистрированы на этапе создания конечного автомата, а те уже проверят самостоятельно, соответствует ли кому-нибудь из них это событие.
При последовательном подключении состояний сохранять объект состояния (QState), из которого должен быть осуществлён переход, необязательно, поскольку в этом режиме работы в конечном автомате (QStateMachine) текущим может быть только одно состояние. Достаточно отправлять лишь сведения о результате выполнения. Но всё меняется, если состояния подключаются параллельно: одно событие переключит сразу нескольких, что во многих случаях приведёт к непредсказуемому результату. Сохранение же указателя на источник перехода позволит избежать обозначенной проблемы.
Осталось поменять код из примера выше для параллельного подключения состояний.
void MyClass::run() { QStateMachine *machine = new QStateMachine(QState::ParallelStates, this);
connect(machine, &QStateMachine::stopped , machine, &QStateMachine::deleteLater); connect(machine, &QStateMachine::finished , machine, &QStateMachine::deleteLater);
for(int i = 0; i < 2; ++i) { QState *parent_state = new QState(machine);
QState *state = new QState(parent_state); connect(state, &QState::entered, [this,state]() { // Код при входе в **i-ое** состояние ... bool result = ... ;
if(result == true) { state->machine() ->postEvent(new StateEvent(state , StateEvent::RESULT_SUCCEED)); } else { state->machine() ->postEvent(new StateEvent(state , StateEvent::RESULT_FAILED)); } });
QFinalState *final_state = new QFinalState(parent_state);
StateTransition *success_transition = new StateTransition(); success_transition->setEventResult(StateEvent::RESULT_SUCCEED); success_transition->setTargetState(final_state);
StateTransition *fail_transition = new StateTransition(); fail_transition->setEventResult(StateEvent::RESULT_FAILED); fail_transition->setTargetState(final_state);
state->addTransition(success_transition); state->addTransition(fail_transition); }
// Активируем машину (указывать точку входа нужды нет). machine->start(); }
Поскольку QStateMachine является наследником QState мы имеем возможность объявить посредством установки флага QState::ParallelStates, что дочерние состояния машины должны быть подключены в параллельном режиме. Это указание действует только на тех из них, для которых объект конечного автомата является непосредственным родителем.
Таким образом в теле цикла в параллельный режим войдут только копии parent_state, тогда как уже их дочерние состояния останутся по умолчанию - подключенными последовательно.
Разумеется, что именно такое построение как в примере вовсе необязательно и объекты QState (в т.ч. QStateMachine) можно компоновать с любым количеством иерархических уровней, однако последний, самый низкий уровень должен быть представлен в виде последовательного подключения как минимум одного QState и QFinalState, поскольку только тогда "наверх" по иерархии будет отправлен сигнал о завершении работы.
Да, можно обойтись даже и одним QFinalState, но тогда необходимо иметь в виду, что сигнал finished() будет отправлен прежде, чем будет выполнен вход в состояние QFinalState, а значит код, ассоциированный с ним, будет также выполнен позднее. Для многих задач это бывает неприемлемо.
Каждый из parent_state в примере будет выполняться параллельно по отношению к другому, и может закончить свою работу раньше, либо позднее других. Однако при этом QStateMachine не остановится, несмотря на то, что от одного из выполнившихся parent_state к нему придёт сигнал finished(). Лишь только тогда, когда все parent_state завершат свою работу, только тогда конечный автомат завершит и свою, отправив finished(), но уже от себя.
QStateMachine чрезвычайно мощный и гибкий инструмент, и совершенно напрасно многие считают его избыточным, либо подходящим лишь для подключения анимации. Этот фреймворк содержится в модуле QtCore и может быть весьма полезен для реализации практически любых задач, требующих, прежде всего, асинхронного выполнения.

JavaScript. Создание объектов с приватными свойствами. Как?

Вникаю в ООП JavaScript. Пытаюсь разобратся в создании обектов с приватными методами и свойствами.
В JavaScript можно создавать обьекты через new
function Constructor() { var name = 'foo'; this.get_name = function() { return name; } } var foo = new Constructor();
А можно без
function ConstructorWithoutNew() { var name = 'bar'; var that = {}; that.get_name = function() { return name; } return that; } var bar = ConstructorWithoutNew();
Первый вопрос - есть ли принципиальная разница между этими подходами? При каких условиях стоит использовать тот или иной, или возможно есть третий, более универсальный/продуктивный/правильный?


Ответ

Разница этих подходов в том, что в первом случае активируются механизмы наследования в js, а во втором случае - используются механизмы утиной типизации.
При использовании второго способа не будет работать оператор instanceof - зато так можно "унаследоваться" от функции, чего нельзя нормально сделать с прототипами.
Но оба способа не являются идеальными - в каждом из них создается отдельный метод на каждый экземпляр объекта. При (ошибочной) попытке применить такой метод на "чужой" экземпляр объекта через call или apply возможны странные вещи, которые будут не сразу очевидны.
Поэтому самый современный способ создания приватный полей - через символы. Кратко: вызов Symbol("...") даст вам некоторый объект, который можно использовать в качестве имени поля, при этом повторный вызов функции Symbol даст уже новый объект. То есть, если "спрятать" символ - то и поле окажется приватным.
Вот пример, здесь символ "спрятан" при помощи паттерна "модуль":
var Constructor; !function() { var sName = Symbol("name") Constructor = function Constructor() { } Constructor.prototype[sName] = "foo" Constructor.prototype.get_name = function() { return this[sName]; } }() var foo = new Constructor();
Переменная sName не видна за пределами модуля - а потому и доступа к приватному полю name снаружи нет, хотя внутри модуля к нему обращаться довольно просто.
Поддерживаются символы на момент написания этого ответа, к сожалению, только Хромом и Огнелисом из основных браузеров. Но для других браузеров (IE) можно использовать полифил:
!function() { if (typeof window.Symbol === "function") return;
var counter = 0; window.Symbol = function (name) { return "__" + name + "@" + ++counter; } }()
Такой символ не будет обладать большинством свойств "настоящих" символов - но для создания почти-приватных переменных вполне пойдет.

Непонятная ситуация с DataGrid

Есть две почти одинаковые ситуации. В первой контекст DbContext создается как поле, получает все данные из базы и этот лист привязывает к DataGrid и все работает. Во второй контекст DbContext создается через using, а дальше все так же, но данные которые помеченные как virtual в DataGrid не отображаются. Почему так ?
UPDATE. Данные не помеченные как virtual тоже не отображаются.
UPDATE 2 Если запустить приложение в режиме отладки из метода ShowData, и подождать пару секунд, то данные отображаются так как и нужно. Если пробежаться быстро по методу в отладке то все по старому.
UPDATE 3. Проблема до сих пор актуальная, хочу понять почему так.
Ситуация 1.
class MainVM:BaseVM { ContentStorage db = new ContentStorage();
public MainVM() { ShowData(); }
private List listStorage;
public List ListStorage { get { return listStorage; } set { listStorage = value; OnPropertyChanged(); } }
private void ShowData() { var s = db.Storages.ToList(); ListStorage = s; }
public MainVM() { ShowData(); } }
Ситуация 2.
class MainVM:BaseVM { private List listStorage;
public List ListStorage { get { return listStorage; } set { listStorage = value; OnPropertyChanged(); } }
private void ShowData() { using (var db = new ContentStorage()) { var s = db.Storages.ToList(); ListStorage = s;
} }
public MainVM() { ShowData(); } }


Ответ

Судя по всему, "данные помеченные как virtual" - это навигационные свойства.
EF по умолчанию не загружает связанные сущности. Вместо этого он создает класс-обертку поверх вашего Storage, переопределяя виртуальные свойства так, что они будут подгружены при первом обращении. Этот механизм называется Lazy Load.
Для его работы нужно чтобы контекст, из которого был загружен объект, был жив на момент обращения к свойству. В первом примере контекст жив - и все работает. Во втором - вы уничтожаете его сразу после получения списка. После уничтожения контекста все такие свойства будут бросать исключение (что, скорее всего, съедается гридом)
Вам стоит или держать контекст живым, или явно подгружать связанные сущности:
var s = db.Storages.Include("MyProperty").ToList();
Или
var s = db.Storages.Include(s => s.MyPropery).ToList();

Сортировка ключей словаря по возрастанию

Приветствую.
Дан словарь
{'2.3': ['0', '1'], '4.9': ['0', '1'], '4.7': ['0', '1'], '3.5': ['0', '1'], '2.4': ['0', '1'], '3.17': ['0', '1'], '4.8': ['0', '1'], '3.6': ['0']}
Необходимо отсортировать его по ключам в порядке возрастания. Пытаюсь сделать так:
for x in sorted(dict_list.keys(), key=lambda x : float(x[:len(x)-1])): print(x)
Получается
2.3, 2.4, 3.5, 3.6, 3.17, 4.9, 4.7, 4.8
В общем, сортирует он явно как-то не так. Подскажите, пожалуйста, как отсортировать в порядке возрастания ключей? Заранее спасибо.


Ответ

Если вы хотите вернуть отсортированный список ключей/значений, можно просто отсортировать словарь по его ключам:
for k in sorted(dict_list.keys()): print (k, ':', dict_list[k])
Если нужно хранить отсортированные пары ключ/значение, попробуйте использовать OrderedDict
from collections import OrderedDict
#отсортирует по возрастанию ключей словаря OrderedDict(sorted(dict_list.items(), key=lambda t: t[0]))
Документация по OrderedDict
UPD по просьбе автора сортировка по возрастанию с условием, что 3.17 должно быть последним
for i in sorted(dict_list.items(), key=lambda x: (len(x[0]), x[0])): print (i[0])
Если 3.17 должно быть первым, можно просто добавить - в условие:
..lambda x: (-len(x[0]), x[0]))

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

можно ли сделать резиновую разметку в приложении, так чтобы добавляемые из кода элементы дополнялись слева на право, с переходом на следующую строку?
макет:
button1 button2 button3 button4 button5
button6 button7 button8 button9 button10
button11 button12


Ответ

То, что вам надо, называется FlowLayout
Воспользуйтесь одной из библиотек: FlowLayout или android-flowlayout
В случае первой либы в разметку добавьте FlowLayout, а в него нужное кол-во ваших виджетов, кои надо переносить на другую строку, если они не вмещаются




Получится примерно так:


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

Mysql возвратить время со сдвигом timezone

Есть ли способ возвращать время в зависимости от временной зоны? Я понимаю что есть timestampz, но в данном случае datetime
т.е. хочу получить время по москве (3) исходя из текущего сохраненного (5) где время по москве 14:00
select dt_create + interval 2 hour from t where dt_create - interval 2 hour = 14:00
но загвоздка в том, что я не знаю какие поля - это время. могу ли я в зависимости от типа поля добавлять интервал?


Ответ

Часовые пояса относятся к уровню презентации. Большинство вашего кода не должно заниматься часовыми поясами или местным временем, оно должно передавать Unix-время как оно есть. Когда измеряете время, измеряйте Unix-время. Это UTC. Его просто получить (системными функциями). Оно не имеет часовых поясов или летнего времени (и високосных секунд). Когда храните время, храните Unix-время. Это одно число. Если вы хотите сохранить время, пригодное для чтения человеком (например, в логах), постарайтесь сохранить его вместе с Unix-временем, а не вместо. Когда отображаете время, всегда включайте в него смещение часового пояса. Формат времени без смещения бесполезен. Системные часы не точны. Вы в сети? Системные часы каждой другой машины не точны по-разному. Системные часы могут, и будут, перепрыгивать вперёд и назад во времени из-за вещей, которые вне вашего контроля. Ваша программа должна быть разработана таким образом, чтобы пережить это. Отношение количества секунд системных часов к количеству настоящих секунд — не точно и может меняться. В основном это зависит от температуры. MySQL хранит столбцы типа DATETIME в виде упакованных в числа значений «YYYYMMDD HHMMSS» Если вы озаботились хранением отметок времени, храните их как целое число и используйте для преобразования функции UNIX_TIMESTAMP() и FROM_UNIXTIME().
Что должен знать о времени каждый программист

Как положить свой ProjectTemplate в раздел VisualSudio (Visual C#/Web)?

Привет! Я пытаюсь затолкать свой шаблон проекта в (Visual C#/Web)(Аналогично тому как там лежит DevExpress Template Gallery на моём скриншоте). Пытаюсь сделать это следующими строками кода в файле MyTemplate.vstemplate:
My Template Gallery Start Template Gallery myProj.ico CSharp Web Web 4.0 0 0 4d72892d-abc3-428d-8fa7-8421a83d6e0a true XafariApplication true


Ответ

Ручной способ
Создаете проект. Заполняете нужными начальными файлами для шаблона. Сохраняете.
В меню Файл выбираете Экспорт шаблона. В Wizard'е выбираете: Project Template. Заполняете поля. Архив закинется в папку <Рабочая папка студии>\My Exported Templates и в папку <Рабочая папка студии>\Templates\ProjectTemplates (если не сняли галку в wizard'е): Этот архив нужно поместить в папку <Рабочая папка студии>\Templates\ProjectTemplates\Visual C#\Web (ее нет, поэтому необходимо создать): Заходите в студию. И Profit.
С помощью VSIX
Дожен быть готов архив. Смотри 1-5 пункты ручного способа Создаете проект VSIX. В проекте создаете нужную иерархию папок. В данном случае получается следующая иерархия: Закидывайте архив в папку ProjectTemplates\CSharp\Web В манифесте указываете нужные данные. Затем заходите в пункт Assets и добавляете: Сохраняетесь. Билдитесь. Запускаете VSIX. Открываете студию.
ProjectTemplate + VSIX
Создаете проект типа ProjectTemplate заполняете его. Выбираете в студии файл с расширением *.vstemplate (по умолчанию его имя совпадает с именем проекта). И в его свойствах в строке Category прописываете путь иерархии, который будет начинаться от CSharp. Т.е. если указываете SubA/SubB/SubC, то шаблон будет лежать в CSharp/SubA/SubB/SubC. В нашем случае указываем только Web: Создаем проект VSIX. В манифесте указываете нужные данные: Билдитесь.
P.S. Немного про элемент TemplateData. Вы указали ProjectSubType значение Web, но этот элемент может иметь только всего 4 значения:
SmartDevice-NETCFv1 - указывает поддержку .NET Compact Framework версии 1.0 SmartDevice-NETCFv1 - указывает поддержку .NET Compact Framework версии 2.0 CSharp - Задается только тогда, когда элемент ProjectType имеет значение Web (т.е. это проект типа веб-сайт). Указывает на языка программирования. VisualBasic - тоже самое, что и предыдущий пункт, только язык программирования VisualBasic

Почему съедает слеш в скобочной группе регулярного выражения?

.htaccess
RewriteRule ^activation-account/(.+)/login([a-zA-Z0-9_]+)$ web/index.php?r=registration/activation-register/activation&key_active=$1&key_login=$2 [L]
Переходим по ссылке http://myhost//activation-account/$2y$10$Xl40W/SKTOXYcxfCxGszr.9tbd//lNJSVJTpBh4umjWf/9GNMSlJy/loginfiko
Ловим переменную в php key_active
Она возвращает: $2y$10$Xl40W/SKTOXYcxfCxGszr.9tbd/lNJSVJTpBh4umjWf/9GNMSlJy
То есть на этом промежутке 9tbd/l убирается слеш а должно быть так 9tbd//l


Ответ

Вот рабочий вариант редиректа для Apache:
RewriteEngine On RewriteCond %{REQUEST_URI} ^/*activation-account/(.+)/login(\w+)$ RewriteRule .* web/index.php??r=registration/activation-register/activation&key_active=%1&key_login=%2 [L]
В переменных запроса он ни чего не имеет против двойного слеша.
UPD + мой ответ на вопрос: Как сделать всё и сразу в mod_rewrite?

Добавить текст в строку ввода

Приветствую. Хочу сделать возможность редактирование переменной, но не нашел функции для добавления текста уже имеющегося значения в строку ввода консоли, для его редактирования. Не подскажете функцию?
P.S Вопрос глупый, но гугл ничего не выдает из-за засилия других глупых вопросов.


Ответ

Вот (довольно искусственный) пример использования GNU readline на Си (также компилируется и g++) (а вот ссылка на нее для windows)
Программа анализирует прочитанную строку и если в ней есть слово, начинающееся с V:, то оно подставляется для ввода следующей строки.
#include #include #include #include
static char *ins;
int f() { if (ins && ins[0]) { rl_insert_text(ins); rl_redisplay(); } }
int main (int ac, char *av[]) { char *line = 0, txt[1000] = "", *p;
rl_pre_input_hook = f; // readline() вызовет нашу функцию перед чтением ввода (после вывода промпта) rl_bind_key('\t', rl_insert); // для вставки символа табуляции (иначе он работает как поиск дополнения имени файла) ins = txt;
while (line = readline("> ")) { printf("line: %s
", *line ? line : ""); txt[0] = 0; if (p = strstr(line, "V:")) sscanf(p, "%s", txt);
if (line[0]) add_history(line);
free(line); }
return puts("End") == EOF; }
Поскольку вложенные функции не поддерживаются в С++ функцию f() (readline hook) и массив char txt[] (или указатель на него, как в этом примере) приходится выносить на внешний уровень.
Пример вызова:
avp@avp-ubu1:hashcode$ g++ t-readline.c -lreadline avp@avp-ubu1:hashcode$ ./a.out > ksdkk line: ksdkk > ueru V:444 line: ueru V:444 > V:444 kksk line: V:444 kksk > End
В строке 5 (третий вызов readline) текст V:444 в начале области ввода подставлен из предыдущей введенной строки.

Отрисовать пиксели на Xorg-окне с помощью стандартных либ в Linux

Графические тулкиты штука сложная, но все они начинают с отрисовки пикселя, мне нужно повторить это базовое действие. Допустим имеется запущенный Xorg, в нём программа, которая любым образом создаёт себе Xorg-окно, в этом окне и нужно рисовать пиксели.
Условие:
Только стандартные библиотеки ядра Linux. Никаких GTK/Qt/OpenGL/Cairo и встроенных Xorg-способов.
Вопросы:
Чем, собственно, рисовать? (Хотелось бы услышать конкретный ответ.) Как рисовать? (Желательны примеры/ссылки на примеры, тема довольно непопулярная, информации мало.) Преимущества того или иного способа.
UPD: В ответ на комментарий пользователя @KoVadim всё не влезло, отпишу в основной вопрос в качестве общего пояснения.
Если это Xorg окно, то и он должен рисовать с помощью XDrawPoint
Хочется сказать, что большинство граф. тулкитов не рисует картинку через фичи графического сервера. Qt рисует с помощью своего "велосипеда", GTK это делает с помощью Cairo, и это правильно. В том же Wayland, который пришел в замену иксов вообще нет подобного функционала. Wayland просто делает окно оставляя отрисовку всего в окне тулкитам.
Остановимся на Cairo, он наверняка использует что-то низкоуровневое, я хочу узнать что. (копать сорцы опыта не хватает)
Фреймбуфер пробовал, всё про него знаю. Во первых лучше использовать DRM. Во вторых это всё не то. (А возможно DRM вполне годен, но как его использовать а Xorg я не нашел, нету документации и всё.)


Ответ

LibXCB считается более современной и более производительной, чем xlib, но документация по ней не полная. Рисовать что-то по пикселам очень непроизводительно, поэтому есть набор примитивов, часть которых обычно поддерживается аппаратно. Там есть пример рисования некоторых примитивов кроме пиксмапов. Cairo использует libXCB.
Кстати если разберётесь, может быть ответите на мой вопрос четырёхлетней давности?

Построение кривой Серпинского без использования рекурсии

Доброго времени суток.
Необходимо реализовать рекурсивную и нерекурсивную реализацию построения кривой Серпинского.
Рекурсивное построение не составило большого труда, а вот найти конечный алгоритм для нерекурсивной реализации (понятный для имплементации) не получилось (Только в книге Род Стивене - "Delphi Готовые алгоритмы").
Может ли кто-нибудь скинуть материалы где можно почитать про нерекурсивную имплементацию построения кривых Серпинского?
Реализация Рекурсивного алгоритма:
static private void Sierpinski(int depth, float dx, float dy, Graphics gr = null){ float x = dx; float y = dy; drawA(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, dx, dy); drawB(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, -dx, dy); drawC(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, -dx, -dy); drawD(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, dx, -dy); }
static private void drawA(Graphics gr, int depth, ref float x, ref float y, float dx, float dy) { if (depth > 0) { --depth; drawA(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, dx, dy); drawB(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, 2 * dx, 0); drawD(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, dx, -dy); drawA(gr, depth, ref x, ref y, dx, dy); } }
static private void drawB(Graphics gr, int depth, ref float x, ref float y, float dx, float dy) { if (depth > 0) { --depth; drawB(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, -dx, dy); drawC(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, 0, 2 * dy); drawA(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, dx, dy); drawB(gr, depth, ref x, ref y, dx, dy); } }
static private void drawC(Graphics gr, int depth, ref float x, ref float y, float dx, float dy) { if (depth > 0) { --depth; drawC(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, -dx, -dy); drawD(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, -2 * dx, 0); drawB(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, -dx, dy); drawC(gr, depth, ref x, ref y, dx, dy); } }
static private void drawD(Graphics gr, int depth, ref float x, ref float y, float dx, float dy) { if (depth > 0) { --depth; drawD(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, dx, -dy); drawA(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, 0, -2 * dy); drawC(gr, depth, ref x, ref y, dx, dy); drawLine(gr, ref x, ref y, -dx, -dy); drawD(gr, depth, ref x, ref y, dx, dy); } }
Реализация функции drawLine()
static private void drawLine(Graphics gr, ref float x, ref float y, float dx, float dy) { if (gr != null) gr.DrawLine(Pens.Black, x, y, x + dx, y + dy); x += dx; y += dy; }


Ответ

Воспользуемся стандартным подходом: перепишем функцию в виде набора заданий и управляющего цикла, который выполняет задания по одному. Вместо рекурсивного вызова функций будем добавлять новые задания в стек этих самых заданий. Этим самым мы как бы заменяем стек вызовов стеком заданий.
Для нашего случая, заведём структуру данных, описывающую одно задание. Одним заданием у нас будет вызов drawA, drawB, drawC, drawD или drawLine. Получаем следующую структуру:
struct DrawTask { public DrawTask(char type, int depth, float dx = 0, float dy = 0) { Type = type; Depth = depth; Linedx = dx; Linedy = dy; }
public char Type; // A, B, C, D или L public int Depth; public float Linedx; // эти параметры нужны лишь для задания типа L public float Linedy; }
Теперь нам нужна очередь заданий и её интерпретатор:
static void Sierpinski_NR(int depth, float dx, float dy, Graphics gr = null) { Stack taskStack = new Stack(); float x = dx; float y = dy;
// загружаем задания в стек в обратном порядке taskStack.Push(new DrawTask('L', depth, dx, -dy)); taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, -dx, -dy)); taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, -dx, dy)); taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, dx, dy)); taskStack.Push(new DrawTask('A', depth));
// пока есть задание, достаём его и выполняем // в результате выполнения в стеке могут оказаться подзадания while (taskStack.Count > 0) { var currentTask = taskStack.Pop(); switch (currentTask.Type) { case 'A': drawA_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'B': drawB_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'C': drawC_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'D': drawD_NR(gr, currentTask.Depth, ref x, ref y, dx, dy, taskStack); break; case 'L': drawLine(gr, ref x, ref y, currentTask.Linedx, currentTask.Linedy); break; } } }
Процедуру A имплементируем таким же образом. Она сама не делает ничего, просто добавляет в стек подзадания:
static void drawA_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth;
// помещаем в стек в обратном порядке taskStack.Push(new DrawTask('A', depth)); taskStack.Push(new DrawTask('L', depth, dx, -dy)); taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, 2 * dx, 0)); taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, dx, dy)); taskStack.Push(new DrawTask('A', depth)); } }
Остальные процедуры реализуются аналогично:
static void drawB_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth; taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, dx, dy)); taskStack.Push(new DrawTask('A', depth)); taskStack.Push(new DrawTask('L', depth, 0, 2 * dy)); taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, -dx, dy)); taskStack.Push(new DrawTask('B', depth)); } }
static void drawC_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth; taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, -dx, dy)); taskStack.Push(new DrawTask('B', depth)); taskStack.Push(new DrawTask('L', depth, -2 * dx, 0)); taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, -dx, -dy)); taskStack.Push(new DrawTask('C', depth)); } }
static void drawD_NR(Graphics gr, int depth, ref float x, ref float y, float dx, float dy, Stack taskStack) { if (depth > 0) { --depth; taskStack.Push(new DrawTask('D', depth)); taskStack.Push(new DrawTask('L', depth, -dx, -dy)); taskStack.Push(new DrawTask('C', depth)); taskStack.Push(new DrawTask('L', depth, 0, -2 * dy)); taskStack.Push(new DrawTask('A', depth)); taskStack.Push(new DrawTask('L', depth, dx, -dy)); taskStack.Push(new DrawTask('D', depth)); } }

Анимация WPF при изменении свойства

Есть класс, представляющий игрока (привожу упрощенный вариант):
class Player : INotifyPropertyChanged { public string Name { get; }
int rank; public int Rank { get { return rank; } set { rank = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Rank")); } }
public event PropertyChangedEventHandler PropertyChanged;
public Player(string name) { Name = name; } }
Коллекция этих Players отображается, ну скажем, в ItemsControl:


Внешние силы меняют рейтинг игрока (не часто, не чаще 1 раза в несколько секунд).
Можно ли сделать средствами анимации WPF (не принципиально) так, чтобы при изменении рейтинга число в TextBox'е менялось плавно от текущего значения до нового, ну скажем, за 0.2 секунды?
Я могу, конечно, в сеттере запускать таймер, но это решение мне не нравится.


Ответ

Такой фичи из коробки нету, но её можно легко смастерить самостоятельно. Например, давайте заведём для этого attached property.
public static class AnimatableDoubleHelper { // Это attached property OriginalProperty. К нему мы будем привязывать свойство из VM, // и получать нотификацию об его изменении public static double GetOriginalProperty(DependencyObject obj) => (double)obj.GetValue(OriginalPropertyProperty); public static void SetOriginalProperty(DependencyObject obj, double value) => obj.SetValue(OriginalPropertyProperty, value); public static readonly DependencyProperty OriginalPropertyProperty = DependencyProperty.RegisterAttached( "OriginalProperty", typeof(double), typeof(AnimatableDoubleHelper), new PropertyMetadata(OnOriginalUpdated));
// это "производное" attached property, которое будет // анимированно "догонять" OriginalProperty public static double GetAnimatedProperty(DependencyObject obj) => (double)obj.GetValue(AnimatedPropertyProperty); public static void SetAnimatedProperty(DependencyObject obj, double value) => obj.SetValue(AnimatedPropertyProperty, value); public static readonly DependencyProperty AnimatedPropertyProperty = DependencyProperty.RegisterAttached( "AnimatedProperty", typeof(double), typeof(AnimatableDoubleHelper));
// это вызывается когда значение OriginalProperty меняется static void OnOriginalUpdated(DependencyObject o, DependencyPropertyChangedEventArgs e) { double newValue = (double)e.NewValue; // находим элемент, на котором меняется свойство FrameworkElement self = (FrameworkElement)o; DoubleAnimation animation = // создаём анимацию... new DoubleAnimation(newValue, new Duration(TimeSpan.FromSeconds(0.3))); // и запускаем её на AnimatedProperty self.BeginAnimation(AnimatedPropertyProperty, animation); } }
Как этим пользоваться? Очень просто. Пусть у нас был такой код:

Заменяем его на следующую конструкцию:

Получается вот что: