Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.
Исходная информация - http://forum.sources.ru. Данный дневник сформирован из открытого RSS-источника по адресу http://forum.sources.ru/yandex.php, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты. По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.[Обновить трансляцию]
swf: Тут должны быть тэги к каждой задаче.
Причём эти тэги должны соответствовать темам школьной программы.
Никакой другой структуры не нужно.
Сейчас не знала, какие тэги написать к 9-й задаче, погуглила, оказалось: кодирование информации, кодирование изображений.
Так что тэги к предыдущим 8-ми задачам нужно исправлять, я их по своему разумению писала.
Что неудивительно, я эти задачи в первый раз в жизни вижу я не сталкиваюсь с задачами по информатике, тем более, школьными.
Не считая ЦТ по информатике, много лет тому назад было дело.
Затем происходит рекурсивный вызов F(7 div 2) = F(3).
Добавлено
Шаг 1. В вызове F(3) есть отложенное действие, поэтому параметр n = 3 тоже помещается в стек:
Шаг 2. Затем происходит рекурсивный вызов F(3 div 2) = F(1).
Так как 1 < 2, то этот вызов завершается без печати.
Шаг 3. Затем происходит рекурсивный вызов F(3 – 1) = F(2), который завершается без печати.
Шаг 4. Из стека выталкивается и печатается 3, вызов F(3) завершён, в стеке находится 7:
Печать: 3.
Затем происходит рекурсивный вызов F(7 – 1) = F(6).
Из-за отложенного действия (печати 6) 6 помещается в стек:
Затем происходит рекурсивный вызов F(6 div 2) = F(3) и повторяются шаги 1-4 (3 помещается в стек, затем удаляется из стека и печатается).
Печать: 33.
Затем происходит рекурсивный вызов F(6 – 1) = F(5), 5 помещается в стек:
Затем происходит рекурсивный вызов F(5 div 2) = F(2), который завершается без печати.
Затем происходит рекурсивный вызов F(5 – 1) = F(4), 4 помещается в стек:
Затем происходит рекурсивный вызов F(4 div 2) = F(2), который завершается без печати.
Затем происходит рекурсивный вызов F(4 – 1) = F(3), 3 помещается в стек:
Все оставшиеся новые вызовы завершаются без печати, затем из стека на печать выходят элементы 3, 4, 5, 6, 7.
Печать: 3334567.
swf: Решение.
Главная характеристика приведённого рекурсивного алгоритма –
наличие отложенного действия.
Прежде чем напечатать n, нужно дождаться, когда завершаться рекурсивные вызовы F(n div 2) и F(n–1).
В ситуации, когда в рекурсивном алгоритме (процедуре) есть отложенное действие, транслятор скрыто от пользователя образует стек и помещает туда все параметры текущего вызова, в данном случае n.
Стек можно представить как трубку с одним запаянным концом. Элементы вталкиваются и выталкиваются из стека через второй открытый конец:
Начинания нашей форумчанки swf - прекрасны, ящетаю! Но, на первый взгляд, малеха хромает системность. Собственно ради этого я и замутил эту тему.
Повестка дня
Как реорганизовать подачу информации, что бы:
1) Она искалась в поисковиках и была актуальна
2) Она была доступна (выкачка pdf или djvu)
3) Она была ситематизирована (разбита по разделам)
Прошу модеров/супермодеров временно "закрепить" эту тему - для "типа поговорить". Послушать мнения завсегдатаев, гостей, и через месяц-другой типа утвердить регламент. Валера, я не вдаюсь в тему монетизации траффика - я там просто нуль, вернее - негатив.
Ну так scope(...) и надо применять только в таких случаях. Я же говорил много раз.
Ну вообще мы объясняли человеку почему лучше в его случае избавится от goto, и обернуть ресурс в RAII обертку, приводили разные примеры, пришол ты и сказал - как хорошо что в D такого говна нет, а есть scope(exit). А теперь ты говоришь - что не говорил что он весь такой из себя ахереть какой.
Я хз как такое можно забыть. Ты же вот прямо только что выделил память, и если ты не страдаешь склерозом, в голове автоматом возникает мысль: "надо освободить ресурс", и ты тут же пишешь scope(exit). С таким же успехом ты можешь забыть написать кастомный делетер в unique_ptr. Вот если у тебя такое встречается много раз, то уже становится неудобно, но в таких случаях и unique_ptr с кастомным делетером становится неудобным.
Когда работаешь с сырым указателем - очень часто забываешь написать delete. Тут примерно тоже самое - потому что это отдельная конструкция. С кастомным делитером - сразу видно будет что ресурс не освобождается, просто по самой конструкции. А этот scope(exit) - не обязательно можно написать сразу после выделения, его можно написать и в конце и в середине кода, уже после выделения памяти под ресурс. А раз так написать можно - значит будь уверен кто нибудь непременно так и напишет. Весь вопрос - как долго будут потом багу искать. Может быть сразу найдут, а может кто то неделю в отладчике проведет. Я вот именно это имею ввиду. Да ты можешь написать - вызывай сразу после выделения памяти под ресурс, но от этого возможность написать его где угодно никуда не девается.
Конечно корректно, ведь конструирование происходит до scope(exit), соотвественно, если при конструировании произошло исключение, то scope(exit) не отработает. Разве это не очевидно?
Ну например там:
Цитата
Установили соединение
Открыли базу
Начали получать доступ к таблице - исключение
scope(exit){
Очистили ресурсы от таблицы
Закрыли Базу
Закрыли соединение
}
Он положился на scope и scope сделал ровно то, что он и ожидал, не больше не меньше. Его "обманул" GC, а не scope.
Мы не знаем кто там кого обманул, вполне возможно что он написал все верно, а потом пришел вася, и вместо создания объекта на стеке как было, взял переделал создание объекта на куче.
Еще раз повторяю - там синтетический пример, обычно такие пишут, чтоб не захламлять лишними деталями и не сбивать людей с толку. Вот допустим у меня проект, хренова туча классов/кода летит какая то ошибка, я не буду все это выкладывать на форум, чтоб спросить что не так. Я возьму напишу минимальный, синтетический пример для воспроизведения проблемы, и выкачу его на форум, там будет 2 класса и три строчки с выводом в консоль, а в реале, может быть сотни классов и строк кода.
Неверно. scope(exit) и GC - вещи ортогональные. В моем же примере с PQClean, GC вообще никак не используется. И повторю еще раз: scope(...) может работать не только с ресурсами (в частности не только с памятью). И также повторю, что все парадигмы C++, в том числе и RAII работают и в D. Ну и какой это сахар, если для его реализации в плюсах надо маленькую либку написать, что и сделал Qraizer.
Ну да, scope ортогонален GC примерно на столько же, на сколько и finally. Для его реализации достаточно написать unique_ptr, можно и класс написать. Qraizer тебе писал аналог в D, чтоб оно и выглядело и юзалось прям как один в один, максимально приближенно. Т.е. по сути он тебе этот сахар и реализовал на плюсах. А так это ничем не отличается от любого класса, в деструкторе которого выполняется то, что у тебя выполняется в теле scope. Потому как оно ровно так же - гарантированно выполняется в конце блока, устойчиво к исключениям, по сути как и finally. Так что scope(exit) - в плюсах это синтаксический сахар, не более. Были бы лямбды в плюсах более лаконичными, хотя бы похожи на делегаты в шарпах, то можно было бы переписать код с std::unique_ptr с кастомным делитером примерно вот так:
auto resource = std::unique_ptr(new CResource(), (e) => {
//! free resource.
delete e;
});
По сути в плюсах все к этому и сводится, если убрать весь этот синтаксический шлак по уточнению типов и указанию шаблонных параметров всяких.
Но на сколько я понимаю в плюсах такое написать нельзя из за скудного RTTI, поэтому и приходится чуть больше закорючек писать.
Приводят ровно такой пример, который в С++ переписывается обычным unique_ptr, в примере free вызывается в scope(exit), в плюсах будет вызываться в кастомном делитере.
Разрабы тащат в языки все что им в голову взбредет, сам факт что разрабы что-то притащили или не притащили вообще не критерий. Тащат говно и наоборот хорошее не тащат сплошь и рядом.
Ну в такие ЯП как JAVА/C# по моему тащат вообще все подряд, что можно затащить.
Ок, вот ты привел там функцию - с виду имитирующуюю RAII, в принципе такой вариант еще ладно, если знаешь что у тебя тут голый ресурс, и ты тут его же удаляешь - такое еще можно оправдать.
Ну так scope(...) и надо применять только в таких случаях. Я же говорил много раз.
Но ведь разные есть случаи, я понял бы если бы это была единная конструкция, хотя бы там через точку например. Но понимаешь ли, бывает так что пишешь new int, и тут же забываешь сделать delete, потому что это две конструкции, независимы друг от друга семантически. Так и тут.
Я хз как такое можно забыть. Ты же вот прямо только что выделил память, и если ты не страдаешь склерозом, в голове автоматом возникает мысль: "надо освободить ресурс", и ты тут же пишешь scope(exit). С таким же успехом ты можешь забыть написать кастомный делетер в unique_ptr. Вот если у тебя такое встречается много раз, то уже становится неудобно, но в таких случаях и unique_ptr с кастомным делетером становится неудобным.
Тут как бы и коню понятно что до второго scope оно не дойдет. Корректно ли отработает первый scope, если исключение произошло на этапе конструирования сложного объекта, вот в чем вопрос.
Конечно корректно, ведь конструирование происходит до scope(exit), соотвественно, если при конструировании произошло исключение, то scope(exit) не отработает. Разве это не очевидно?
Имелось ввиду, что ты можешь положится на scope, ну раз оно от RAII вообще никак не отличается по твоим словам, а гдето выше другой чувак понадеется на полноценный деструктор, и это приведет к ошибке, как в примере по ссылке.
Он положился на scope и scope сделал ровно то, что он и ожидал, не больше не меньше. Его "обманул" GC, а не scope.
Я его притащил - показать с какими проблемами можно столкнуться, полностью положившись на него.
Наоборот, он столкнулся с проблемой, потому что не полностью положился на scope, а понадеялся на деструктор. Если бы он полностью положился на scope и написал бы в нем TTF_CloseFont() и TTF_Quit(), то все бы работало без сегфолтов.
В С++ нет scope, потому что там нет GC, а в D есть GC, поэтому там и scope актуален. Для С++ этот scope(exit) - не более, чем синтаксический сахар.
Неверно. scope(exit) и GC - вещи ортогональные. В моем же примере с PQClean, GC вообще никак не используется. И повторю еще раз: scope(...) может работать не только с ресурсами (в частности не только с памятью). И также повторю, что все RAII-парадигмы C++ работают и в D. Ну и какой это сахар, если для его реализации в плюсах надо маленькую либку написать, что и сделал Qraizer.
Да и в такие языки любят тащить все попало, но scope(exit) - я там как то не встречал. Видимо даже там разрабы посчитали - что излишне вводить то, что уже есть в языке.
Разрабы тащат в языки все что им в голову взбредет, сам факт что разрабы что-то притащили или не притащили вообще не критерий. Тащат говно и наоборот хорошее не тащат сплошь и рядом.
Если бы он все верно оценил, он бы так не написал. Очевидно, что он думал, что деструкторы объектов на куче вызываются по тем же правилам, что и деструкторы объектов на стеке.
Это тебе очевидно и синтетического примера в 5 строчек. Если бы все было просто как ты пишешь, баги делали бы только студенты по своему не знанию. На практике у него может быть довольно не примитивный код, чтоб верно оценить все ньюансы. Именно по этому писать руками дополнительную строчку - может являтся источником ошибок.
Ок, вот ты привел там функцию - с виду имитирующуюю RAII, в принципе такой вариант еще ладно, если знаешь что у тебя тут голый ресурс, и ты тут его же удаляешь - такое еще можно оправдать. Но ведь разные есть случаи, я понял бы если бы это была единная конструкция, хотя бы там через точку например. Но понимаешь ли, бывает так что пишешь new int, и тут же забываешь сделать delete, потому что это две конструкции, независимы друг от друга семантически. Так и тут.
Ну так они и пишут, в деструкторах оберток и кастомных делетерах unique_ptr.
Это пишется в одной конструкции. Если было бы все так просто - все эти RAII нахер бы никому не упали. Какая проблема вызвать delete, после выделения ресурсов - да абсолютно никакой. И тем не менее в том же С++ утечек куча, из за того что люди полагаются на свои руки и память, а потом выхватывают утечки.
Нет в сяшке не тот же. Вызовуться только те scope(), которые успели отработать. Поведение scope(exit) в плане очистки ресурсов полностью совпадает с unique_ptr с кастомным деструктором. Похоже, ты не совсем понял оно работает. Вот посмотри очень короткий пример: https://run.dlang.io/is/7jJE7b
Тут как бы и коню понятно что до второго scope оно не дойдет. Корректно ли отработает первый scope, если исключение произошло на этапе конструирования сложного объекта, вот в чем вопрос.
Я это понял так, что ты решил, что scope(exit) как-то влияет на это.
Имелось ввиду, что ты можешь положится на scope, ну раз оно от RAII вообще никак не отличается по твоим словам, а гдето выше другой чувак понадеется на полноценный деструктор, и это приведет к ошибке, как в примере по ссылке.
Ну я не тащил сюда всякие странные багнутые куски кода. А так да, в C++ нет scope, но есть RAII, а в D есть и scope и RAII.
Я его притащил - показать с какими проблемами можно столкнуться, полностью положившись на него. В С++ нет scope, потому что там нет GC, а в D есть GC, поэтому там и scope актуален. Для С++ этот scope(exit) - не более, чем синтаксический сахар.
Я не сразу понял. Когда я писал try/catch/finally я имел в виду совокупность всех возможных комбинаций и так же понял и твое try/catch/finally.
Важно понимать, что scope(success)/scope(failure)/scope(exit) похожи на try/catch/finally, но отличия достаточно большие.
По сути все отличия сводятся к удобству использования. Не надо просто писать все эти try/catch/finally, но например тотже try-with-resources/using - уже куда более удобны этих конструкций try/catch/finally, и вполне себе могут даже потягаться со scope(exit).
Да и в такие языки любят тащить все попало, но scope(exit) - я там как то не встречал. Видимо даже там разрабы посчитали - что излишне вводить то, что уже есть в языке.
swf: ЕГЭ по информатике 2020, вариант Москва
Рекурсия, результат работы алгоритма
Часть 1, № 11
Задание взято с сайта http://kotolis.ru/realegeinf_2020
Условие. Запишите подряд без пробелов и разделителей все числа, которые будут напечатаны на экране при выполнении вызова F(7). Числа должны быть записаны в том порядке, в котором они выводятся на экран.
Рекурсивный алгоритм F:
procedure F(n: integer);
begin
if F(n) >2 then begin
F(n div 2);
F(n–1);
write(n)
end;
end;
Там, кстати, не собираются делать аналог try-with-resources?
Нет, и не думаю, что станут: try-with-resources работает только в рамках вызова процедуры и одного потока (одной горутины) — слишком узкоспециализированный механизм. Кроме того, try-with-resources в Java работает только если класс реализует
interface AutoCloseable {
void close() throws Exception
}
а разные «ресурсы» могут иметь разные методы «захвата/освобождения» в соответствии с их семантикой: Open/Close, Lock/Unlock, Create/Delete и т.п. Либо заставлять всех использовать всегда один метод (Close, там или Release) — как-то тупо. Вводить в язык явное понятие ресурса с каким-нибудь спец-синтаксисом, типа помечать освобождающий метод каким-нибудь ключевым словом или спец-символом — ну хз. И это всё равно никак не поможет с разделяемыми ресурсами, например:
package main
import (
"fmt"
"sync"
"time"
)
const nWorkers = 5
func main() {
fmt.Println("ok1:")
ok1()
fmt.Println("\nok2:")
ok2()
fmt.Println("\nnot ok:")
notOk()
}
func ok1() {
var wg sync.WaitGroup
wg.Add(nWorkers) // acquire
for i := 0; i < nWorkers; i++ {
go func(x int) {
defer wg.Done() // release
doSomeWork(x)
}(i)
}
wg.Wait()
}
func ok2() {
var wg sync.WaitGroup
for i := 0; i < nWorkers; i++ {
wg.Add(1) // acquire
go func(x int) {
defer wg.Done() // release
doSomeWork(x)
}(i)
}
wg.Wait()
}
func notOk() {
var wg sync.WaitGroup
for i := 0; i < nWorkers; i++ {
go func(x int) {
wg.Add(1) // acquire
defer wg.Done() // release
doSomeWork(x)
}(i)
}
wg.Wait()
}
func doSomeWork(x int) {
<-time.After(1 * time.Second)
fmt.Println("done #", x)
}
=>
ok1:
done # 2
done # 0
done # 4
done # 3
done # 1
ok2:
done # 0
done # 2
done # 1
done # 3
done # 4
not ok:
Program exited.
try-with-resources тут никак не поможет, т.к. позволяет описать только вариант notOk.
А в JDK, ЕМНИП, у HttpURLConnection освобождающий метод называется disconnect, а не close и соответствено класс не реализует AutoCloseable и использовать его в try-with-resources нельзя :facepalm:
Это освобождение в моей обертке фигурировало буквально внутри одной функции.
И потом еще неоднократно повторял. Но ты походу писатель а не читатель :) Впрочем, я тебя понимаю, кому охота читать портянки бессодержательных споров с Килей.
Если мы это сделаем, то это будет уже другой подход. Понимаешь?
Конечно. Но ты же сам придумал это сравнение? Изначально-то речь шла о ситуации, когда у тебя выбор самому писать одноразовую RAII-обертку или воспользоваться одноразовым же scope(exit). А тут ты приводишь пример готовой RAII-обертки из стандартной либы да и еще подразумевая многократное ее применение.
Зато есть странно выглядищий для людей не знакомым с паттерном вызов функции с передачей лямбды, хотя логично делать эти действия на том же уровне, поскольку контекст тот же и мы не специфицируем никакое поведение.
Лямбда подхватывает текущий контекст. Кроме того лямбда вообще может выглядеть просто как скоуп, который лочится. Например в D:
mutex.locked({
...
});
Лично мне это видится более естественным, чем объявление переменной-гварда. Вызов функции любому программисту понятен: внутри что-то делается, по названию функции можно понять что именно делается. А в случае с объявлением переменной-гварда, нужно знать, что это RAII-объект. Потому что переменная объявлена и ничего с ней больше не происходит. Неплюсовик ожидает, что если переменную объявили, то значит ее собираются использовать, если же ее не используют, то это скорее всего ошибка (даже ворнинг такой есть unused variable). То бишь, в данном случае объявление переменной используют не по общепринятому назначению. Вот это я и называю костылем.
Я тебя уже просил привести свое определение термина "костыль".
Нет, сначала ты приведи свое определение термина "определение термина". :)
Все субъективно, я привел свое мнение. А так вон фанаты сяшки или, прости господи, Zig будут называть любой твой RAII - вселенским злом и страшным костылем. И аргументы у них найдутся из серии: в этом вашем RAII-объекте непонятно что происходит, а в моей милой функции все явно написано. И хрен ты их переубедишь :)
Я высказал свое мнение и написал, что именно считаю костылем. Тебе это не показалось костылем - ну и флаг тебе в руки, что еще ты от меня хочешь? :D
Мы же тут просто мнениями обмениваемся и не ставим цели учить писать программы.
Если ты используешь scope(exit) просто в реализации bracket pattern, то для работы с ресурсом ты используешь bracket pattern, а не scope(exit), понимаешь? scope(exit) у тебя просто часть реализации, там вооще мог быть try...catch, это не имеет принципиального значения
Впрочем, я писал уже, что scope(exit) лучше finally, хотя и не принципиально.
Да-да, так и есть. И scope(exit) очень удобно применять для реализации этого, хм паттерна, в языках, в которых существют исключения и ранний return.
До этого обсуждения ни разу не слышал, что это паттерн и что у него, оказывается, даже есть название. Хотя это похоже какой-то чисто хаскелевский мемасик.
Этот PQClear встречается во всей моей обертке вокруг libpq ровно один раз, а именно в scope(exit) в одном из методов класса Connection.
Это было не очевидно из твоего кода. Если так, то спорить тут не о чем.
Цитата
Тогда двай уж и scope(exit) вынесем в отдельную функцию
Если мы это сделаем, то это будет уже другой подход. Понимаешь? :)
Цитата
И знаешь чем второе лучше первого?
Я достаточно подробно описал твое "второе" в предыдущем ответе тебе. И даже сказал, что не вижу в этом подходе ничего плохого, просто RAII более универсальный подход.
Цитата
Нет бесполезного и странно выглядящего для людей не знающих C++ объявления переменной lock, которая нигде потом явно не используется.
Зато есть странно выглядищий для людей не знакомым с паттерном вызов функции с передачей лямбды, хотя логично делать эти действия на том же уровне, поскольку контекст тот же и мы не специфицируем никакое поведение.
Зависит от того, к чему привык конкретный программист, который будет читать код. Вкусовщина.
Цитата
Самый что ни на есть настоящий костыль.
Я тебя уже просил привести свое определение термина "костыль". Никакой принципиальной разницы тут нет, поскольку и в случая RAII и в случае bracket pattern ты освободишь ресурс автоматически. В первом случае за счет "лишнего" объекта, во втором - за счет "лишних" вызова функции и лямбды.
Цитата
заверни scope(...) в отдельную функцию (получится тот же using/with/try-with-resources, только лучше, потому что гибче) или делай RAII.
Если ты используешь scope(exit) просто в реализации bracket pattern, то для работы с ресурсом ты используешь bracket pattern, а не scope(exit), понимаешь? scope(exit) у тебя просто часть реализации, там вооще мог быть try...catch, это не имеет принципиального значения ;)
Впрочем, я писал уже, что scope(exit) лучше finally, хотя и не принципиально.
И чем это будет отличаться от классического RAII? Ты ещё не забыл о главном преимуществе scope()ов, за которое радеешь?
Тем, что написать такую обертку проще, чем RAII-класс. Ну и в случаях вроде лока мютексов еще и нет бесполезных объявлений переменных. Посмотри на свою иммитацию scope: scope1, scope2, scope3... - что это за мерзкие нумерованные переменные, которые нигде не задействованы? Вот где костыль-то.
но вдруг и внезапно – скоуп может захотеть владеть разными ресурсами, в разном количестве и с разной стратегией владения.
Если скоуп внезапно захочет чего-то сложного, то внезапно убирается scope(...) и делается та самая RAII-обертка. Но на моей практике такого никогда не было. Если я принял решение использовать scope(...), то в будущем так и не возникала необходимость его менять на RAII.
Это называется оверинжиниринг, когда код усложняется "на всякий случай", даже когда вероятность возниконовения этого "всякого случая" в будущем близка к нулю. Я сам страдаю таким и раньше мне приходилось местами заставлять себя писать scope(exit) вместо RAII-обертки. Потому что объективная оценка показывала, что в данном случае "вдруг и внезапно скоуп никогда не захочет владеть разными ресурсами, в разном количестве и с разной стратегией владения." Сейчас уже почти привык не городить абстракции и обобщения там, где они вероятно не понадобятся никогда.
Исполнение данных тобой инструкций – да, автоматическое. С этим и __attribute__((__cleanup__())) справляется. Интересно, почему его не включали в Стандарт C? Это же так удобно, по твоим словам.
А по твоему удобнее эти goto? Да и cleanup гораздо примитивнее scope(exit).
А так сишники народ странный, но думаю многим из них понравился бы scope(exit). А Стандарт это дело консервативное и тормозное. Сколько ждали появления в плюсах лямбд и auto?
swf: В мединституте на госэкзамене студенту-дипломнику показывают два скелета - мужской и женский: - Кто это? Студент молчит. - Как, вы и этого не знаете? Чему вас шесть лет учили? Тот робко: - Маркс и Энгельс, да?