Страницы

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

среда, 21 ноября 2018 г.

Рекурсивные запросы, списки смежности

есть таблица хранящая вершины в виде списков смежностей
Keyword(id INT PRIMARY KEY, value TEXT, parent_id INT REFERENCES Keyword DEFAULT NULL);
нужно посчитать для каждой вершины размер ее поддерева т.е. для данных
id parent_id ------------- 0 NULL 1 0 2 0 3 1 4 1
будет
id size ------------- 0 5 1 3 2 1 3 1 4 1
по идеи можно пройти по списку смежности, с использованием массива, который хранит путь потом используя операнды @> или <@ посчитать размер поддерева.
Т.е. есть пути 0-1-3, 1-3, 0-8, 0-1-4, 1-4
и для 0 размер будет 5
не пойму как для каждого узла хранить масив определяющий путь без использования функций


Ответ

WITH RECURSIVE q AS ( select id,id as idx from Keyword union all select K.id,q.idx as idx from Keyword K, q where K.parent_id=q.id ) select idx,count(1) from q group by idx order by idx
Вот так в postresql выглядят рекурсивные запросы. Внутри RECURSIVE всегда должен быть union. Первая часть union должна выбрать стартовые записи, с которых рекурсия начнется. Например, если бы нам надо было посчитать не количество потомков, а получить всех потомков записи с id=0 мы бы в первой части задали условие where id=0. Вторая часть union это основная рекурсивная часть. Она осуществляет спуск по дереву, выбирая записи из with и при этом поставляя собственные выбранные записи в этот же with, после чего она их там видит и использует уже их id. Что бы правильно посчитать записи нам надо знать изначальный id с которого начала выбираться ветвь, для этого приходится в первом подзапросе выбрать id второй раз и назвать его по другому, потому как просто q.id равен parent.id и у потомка это будет id его непосредственного родителя. А выбрав id под другим именем, не указанным в where мы получаем именно изначального родителя.

Как удалить произвольный элемента стека Forth

Как удалить произвольный элемент из стека данных в языке Форт?


Ответ

N ROLL DROP
где N — номер элемента от вершины стека начиная с 0.
Например, фраза 4 3 2 1 0 3 ROLL DROP оставит на стеке 4 2 1 0
Такой вариант слова ROLL закреплен в спецификации начиная с Forth-83

слушатель на checkbox из listview не работает

Создал listView с собственной разметкой, привязал к SimplApdapter А в разметке есть checkbox,и мне нужно поставить на него слушатель.Я испробовал все, не получается.Как решить данную проблему?
ListView listView = (ListView) findViewById(R.id.listView); list = new ArrayList

Ответ

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

Можно ли как то программно заставить роутер переподключиться?

Можно ли как-то программно заставить роутер переподключиться?
WAN-соединение (динамический ip-адрес). хочу иметь возможность, запустив скрипт, получить от провайдера новый ip-адрес. хотелось бы увидеть код на любом языке.


Ответ

Это можно сделать разными способами.
Через HTTP. Отловите запросы, которые делает ваш браузер когда вы заставляете роутер получить новый IP вручную - и воспроизведите их в скрипте. Желательны знания протокола HTTP. Отловить эти запросы можно через инструменты разработчика в браузере или через Fiddler Через telnet или ssh. Подключитесь к роутеру по этим протоколам и попробуйте найти нужную команду. В скрипте достаточно послать ее еще раз. Обычно скрипты для такого способа выходят проще - но поиск решения сложнее из-за отсутствия документации. Желательны знания UNIX. Для подключения можно воспользоваться Putty Через UPnP. Винда же как-то умеет интернет отцеплять и подцеплять обратно - значит, и для вас способ должен быть. Но тут подробностей я вообще не знаю. Нужны знания UPnP, которых у меня нет.
Более конкретных советов дать не могу: разные роутеры очень сильно отличаются друг от друга, единых стандартов нет (кроме UPnP). Даже одна и та же модель роутера, выпущенная в разные годы, может работать по-разному.

Предоставляет ли провайдер VPN-доступ? Если да, то можно отказаться от интернета по DHCP, подключить себе VPN - а переподключать VPN скриптом куда проще.
Еще один альтернативный вариант - TOR. Видимый IP-адрес будет меняться при каждой смене выходной ноды. Возможно, клиент можно и как-нибудь пнуть для принудительного изменения выходной ноды (перезапуск клиента - универсальный способ). Для использования этого варианта понадобится научиться работать через прокси-сервер.

Отклик на касание по cardview

Есть список кликабельных CardView построенных с помощью RecyclerView и кастомного Адаптера со стандартной реализацией. ViewHolder имплементирует OnClickListener. Отсутствует анимация "касания" элемента. Т.е. при нажатии на любое cardView нет никакого отклика, что нажатие действительно произошло, срабатывает только лишь один OnClickListener.
Как можно прикрутить анимацию ripple эффекта? Вероятно есть уже готовые реализации от самого гугла, но найти ничего не получается почему-то, кроме такого. Попробовал сделать, анимация действительно появилась, но теперь не работает метод OnClickListener :) Вернее, работает, но только если нажимать на края cardview (видимо там где у меня определен layout_margin и куда анимация нажатия не достает). Как можно решить проблему? Спасибо
разметка cardview.xml


Ответ

Видимо это особенности разработки под Android, которые нужно просто знать.
Убрал из разметки android:clickable="true" и все заработало как нужно. Надеюсь кому-нибудь поможет в будущем.

Моя реализация долго выполняющейся задачи с прогрессом и ее слабые места

Ситуация: имеется приложение asp.net mvc. Есть кнопка по нажатии на которую запускается процесс генерации некоего отчета. Отчет может генерироваться довольно долго например несколько минут. Необходимо также после запуска генерации показать пользователю модальное окно с прогрессом выполнения генерации и возможностью отмены. Я реализовал это так.
private static readonly IList DownloadStates = new List();
public ActionResult Export() { var task = new DownloadTask(Guid.NewGuid()); DownloadStates.Add(task);
string zipFilename = string.Format("{0}.zip", task.Id); GenerateAsync(zipFilename, task);
return Json(task.Id, JsonRequestBehavior.AllowGet); }
Этот экшен вызывается при нажатии на кнопку генерации отчета. Тут происходит вот что. Сначала создается задача task. Код этого класса выглядит так
public class DownloadTask { public DownloadTask() { }
public DownloadTask(Guid id) { Id = id; State = DownloadState.NotStarted; CancellationTokenSource = new CancellationTokenSource(); }
public Guid Id { get; set; }
public double CurrentProgress { get; set; }
// состояние задачи - запущена, отменена, ошибка, завершена и тд public DownloadState State { get; set; } }
Задача помещается в коллекцию DownloadStates.
Вот так выглядит код асинхронного метода GenerateAsync
public async Task GenerateAsync(string filename, DownloadTask currentTask) { await Task.Factory.StartNew(() => { try { currentTask.State = DownloadState.Processing;
foreach (var page in pages) { // обновление прогресса currentTask.CurrentProgress = // ... // обработка данных } currentTask.State = DownloadState.Completed; } catch { currentTask.State = DownloadState.Faulted; // обработка ошибки } }).ConfigureAwait(false); return currentTask.Id; }
То есть нажав на кнопку мы отправляем запрос на генерацию отчета. Создается задача, ей присваивается уникальный Guid, задача помещается в коллекцию задач DownloadStates и после этого запускается асинхронный метод GenerateAsync. После запуска этого метода сервер отправляет в виде ответа клиенту guid созданной задачи. Асинхронный метод что важно запускается без await по принципу fire and forget. Дальнейшая связь с ним процессом генерации происходит через коллекцию DownloadStates.
После этого на клиенте когда пришел ответ сервера с идентификатором задачи запускается функция setInterval в которой каждые 200 мс выполняется запрос к серверу чтобы узнать прогресс выполнения задачи и отобразить этот прогресс в модальном окне. Вот код экшена отвечающего за получение текущего прогресса
public ActionResult Progress(Guid taskId) { var task = DownloadStates.FirstOrDefault(x => x.Id == taskId); string state = ""; bool isFaulted = false; bool isCancelled = false;
if (task != null) { switch (task.State) { case DownloadState.Faulted: isFaulted = true; DownloadStates.Remove(task); break; case DownloadState.Processing: state = "processing"; break; // ... прочие ветки } } else isFaulted = true;
return Json(new { progress = task != null && task.State != DownloadState.Completed ? task.CurrentProgress * 100 : 100, text = state, isFaulted, isCancelled }, JsonRequestBehavior.AllowGet); }
Здесь происходит следующее: если задача выполняется то в ответе возвращаем клиенту текущий прогресс (он хранится в таске в коллекции DownloadStates а обновляется внутри асинхронного метода). Если во время выполнения асинхронного метода произошло какое-то исключение, то клиенту возвращается isFaulted = true и клиент получив это значение останавливает таймер и прекращает слать запросы о прогрессе (задача завершилась с ошибкой) и отображает сообщение об ошибке. Если же задача имеет статус Completed то прогресс устанавливается в 100% и таймер на клиенте также останавливается после чего выполняется запрос на загрузку сформированного отчета.
Вкратце это работает вот так как я описал. Эта схема действительно работает но у меня нет большого опыта с написанием подобного асинхронного кода и поэтому меня мучают сомнения правильно ли я все сделал? Например при вызове асинхронного метода GenerateAsync я не вызываю await чтобы не дожидаться его завершения а сразу вернуть клиенту идентификатор новой задачи. Поэтому узнать об ошибке я могу только через установку свойства isFaulted у моих задач в коллекции, хранящейся в контроллере и чтение этого свойства при получении текущего прогресса.
Хотелось бы услышать мнение более опытных программистов насчет потенциальных проблем моего кода (утечки памяти, незавершенные таски, необработанные исключения, может быть где-то надо было поставить lock или могут возникнуть блокировки или что-то еще?). Есть ли тут какие-то слабые места (а они я уверен есть), что тут стоило бы улучшить особенно с учетом многопоточного выполнения и потенциального запуска нескольких задач одновременно. Заранее спасибо!
P.S. к сожалению использование SignalR оказалось невозможным по независящим от меня причинам поэтому вариант переделать все на SignalR я рассматривать не могу


Ответ

Основные проблемы
Нет lock на обращениях к DownloadStates. Класс List не потокобезопасен, доступ к нему нужно вручную синхронизировать. Task.Factory.StartNew не обязательно запускает таск в новом потоке. Вместо него стоит использоватьTask.Factory.Run Единственный более-менее надежный метод выполнения фоновых задач в ASP.NET - это QueueBackgroundWorkItem Ваш подход не слишком применим в живых приложениях, при наличии двух и более web-серверов, и, соответственно, двух копий приложения.
Гораздо надежнее не запускать фоновые задачи в самом ASP.NET, а положить задания в базу данных, и сделать win-сервис, который будет их оттуда доставать, выполнять, и отчитываться через базу о прогрессе.

Подробнее:
Синхронизация списков
List работает на простых массивах внутри. Т.е. поиск FirstOrDefault в нем сделан обычным перебором элементов по индексу от 0 до N-1.
Предположим у вас запустилось два таска - 0 и 1.
Таск 0 выполнился. Пришел запрос на Progress для Task 0. Дошел до строчки с Remove Пришел запрос на Progress для Task 1. Зашел в поиск FirstOrDefault, дошел до индекса 1 Запрос на Progress для Task 0 взял и удалил свой элемент из списка! В Progress для Task 1 вызов FirstOrDefault... падает? возвращает null?
QueueBackgroundWorkItem
В ASP.NET есть механизм рейсайкла апппула. В определенный момент IIS может взять и перезапустить процесс, в котором выполняется приложение. По умолчанию это происходит раз в 29 часов. Может происходить по лимиту памяти. Или по неактивности. Обычный таск из Thread Pool или любой другой фоновый поток будет просто прибит при ресайкле, мгновенно и без каких-либо уведомлений.
Таск, созданный через QueueBackgroundWorkItem, получит уведомление о выключении (через CancellationToken). Кроме того, у него будет 90 секунд чтобы на это уведомление отреагировать. Не сверхнадежно, но лучше чем ничего.
Проблемы с двумя Web-серверами
Если у вас два бэкенда - то у вас две независимых копии приложения. Два списка DownloadStates. Если запрос о старте таска пришел на один сервер, а запрос прогресса - на другой - то пользователь получит isFaulted.
Решается переносом очереди закачек в базу или в любое другое общее хранилище (например, сервис с очередью).

Опкод команды mov/xor

В листинге дизассемблированного файла имею:
.text:00401296 cmp eax, 0 .text:00401299 jz short loc_4012B0
Пронзив исполняемый файл hex-редактором, лечу на адрес 696 (адрес первой команды cmp) и там смотрю такие байты:
83 F8 00
Пытаюсь разобраться.. Топаю на http://sparksandflames.com/files/x86InstructionChart.html Там мне подсказывают, что
83 - SUB Ev Ib...
Вычитание? Не сравнение? В самой программе он работает как сравнение.. Привычный мир рушится...
Вопросы:
Почему 83 - вычитание, а не сравнение или опкод "модифицируется" каким-то спец. байтом? Как вычислить опкод команды mov eax,eax (не 89 F8 00 случаем?).
Как в этом случае будет влиять на ход программы байт mod r/m? Или xor eax,eax? (не 35 F8 F8 случаем?)
Видимо, просто запутался очень + не хватает знаний и нормально изложенного материала. Направьте, камрады.


Ответ

Команда целиком состоит из трех байт: 83 F8 00
83h - это только часть опкода. Как уже заметил VladD, это может соответствовать командам ADD, OR, ADC, SBB, AND, SUB, XOR и CMP. Чтобы понять, что это за команда фактически, нужно смотреть следующий байт (F8h). Это байт MOD R/M, состоящих из 3 полей (по 2, 3, 3 бита):
F8 == 11 111 000
По второму полю этого байта можно определить конкретную команду (хотя, обычно оно отвечает за один из операндов) - как раз из списка ADD, OR, ADC, SBB, AND, SUB, XOR и CMP. Первое поле показывает, что второй (на самом деле первый) операнд - это регистр (или адрес в памяти, но в данном случае это все же регистр). 000 - это, собственно, код регистра, EAX.
Третий байт является вторым операндом команды - 0.
Больше по этой теме можно почитать, например, здесь: Крис Касперски - Дизассемблирование в уме
А еще лучше - читать руководства от Intel в оригинале
Используемый вами справочник очень не полный.
Как вычислить опкод команды mov eax,eax?
Скомпилировать и посмотреть, это наиболее надежный способ, или смотреть в руководстве от Intel.
Или xor eax,eax? (не 35 F8 F8 случаем?)
Скорее 31 C0. Второй байт - байт mod r/m, там просто указывается, что производится операция производится между регистром и регистром, и коды самих регистров:
11 000 000
(000 в обоих случаях - обозначение регистра eax). А первый байт - не 35h, а 31h, т.к. операция не между регистром/памятью и значением, а между регистром и регистром/памятью.