Лямбда функции c рекурсия
Я пытаюсь написать рекурсивное лямбда-выражение для прохождения этой следующей структуры.
Я хочу написать свое лямбда-выражение, чтобы установить его для всех дочерних и дочерних элементов. пожалуйста помоги.
Я также открыт для альтернативного решения, если это невозможно.
4 ответа
Вы не можете построить рекурсивный процесс только с LINQ.
Вам понадобится рекурсивная функция.
На мой взгляд, это подходящее и элегантное решение:
Traverse - рекурсивная функция, которая проверяет узел и вызывает все его дочерние узлы.
Check - это просто функция, которая вызывает Traverse для каждого узла по заданному списку идентификаторов.
Например, вы можете обернуть его в публичный статический вспомогательный класс, и им будет удобно пользоваться:
Затем вы можете использовать его так:
Это не Linq, но один из способов сделать это. С учетом того, как разработан LInq, ваш вопрос должен быть невозможен
Это решение использует сумку для хранения списка всех родителей, которые уже проверены. Это не очень эффективно, но для небольших коллекций подойдет.
Обратите внимание, что я сначала сортирую по идентификатору родительского узла, чтобы добраться до родителей до детей. Также обратите внимание, что я добавляю в корзину недавно найденных родителей.
Вы можете написать предложение where примерно так:
Ознакомьтесь с полным демонстрационным примером здесь
Может быть, это то, что вы ищете:
Извините, я не знаю объявление вашего типа, содержащее NodeId , Parent и IsChecked . Так что я немного догадался.
Возможно, вы можете добавить метод расширения для IEnumerable , чтобы иметь ForEach и удалить вызов ToList , который создает копию вашей коллекции.
Но студия 2012 не способствует моему желанию. Как ее (или меня?) научить делать рекурсивные лямбда функции?
ой, это же зис-в-лямбде-проблема!
по старинке можно так
так не интересно. Хочу именно лямбду
Тип лямбды укажи как std::function , а не auto, ну и захват контекста не забудь — переменная foo объявлена за пределами лямбды.
Полноценный пример. Вполне себе работает.
Volodar
тогда напиши игрек-комбинатор и сделай с его помощью
Полностью законченный вариант, готовый к in-place внедрению:
для говнокода уже годится
напишите с игрек-комбинатором
TarasB
> напишите с игрек-комбинатором
laMer007
Ну не ценит он выразительной красоты синтаксической крестомощи. =)
Но вообще конечно говнокод. Привёл я его потому что он полностью удовлетворяет вопросу сабжа. Ибо лямбда по определению есть анонимная функция не имеющая идентификатора в точке встраивания.
Ах блин, нет же, то что я привел есть UB. Там же ссылка застревает на переменную из завершенной функции.
Но однако же исходные материалы для действительно работающей лямбды все уже на руках.
Лямбда тут интересна тем что помогает имитировать конструкцию let x = . in x из забыл уже какого ФЯ, т.е. создавать закрытый от внешнего мира неймспейс в котором фигачить можно любой код и возвращать результат. В т.ч. заводить промежуточные переменные.
Но вернуть то надо value-результат без ссылок на вовнуть. А значит скрещивать надо не лямбду с лямбдой, а полноценный функтор с лямбдой:
Вот это уже железобетонная лямбда.
P.S.
Кстати, этот вариант просто за счёт отсутствия std::function в релизе заинлайнился полностью и всецело до констаты. Забавно, std::function у gcc способность к инлайну сильно подрезает.
=A=L=X=
> Забавно, std::function у gcc способность к инлайну сильно подрезает.
clang тоже генерирует жуткий код
=A=L=X=
> Лямбда тут интересна тем что помогает имитировать конструкцию let x = . in x> из забыл уже какого ФЯ
Haskell
Лямбда-выражения. Общие понятия о лямбда-выражениях. Лямбда-оператор. Одиночные и блочные лямбда-выражения
Содержание
Поиск на других ресурсах:
1. Какие есть способы создания анонимных функций?
Более подробно работа анонимных методов описывается в темах:
⇑
Цель использования лямбда-выражений точно такая же как и анонимных методов. Лямбда-выражения являются альтернативой анонимным методам.
Лямбда-выражения позволяют программировать функции в упрощенном виде без использования имени с помощью специального оператора, который обозначается ‘=>’ .
⇑
3. Что такое лямбда-оператор?
⇑
4. Какие существуют виды лямбда-выражений?
- одиночные лямбда-выражения;
- блочные лямбда-выражения.
⇑
5. Какая общая форма объявления одиночного лямбда-выражения? Примеры
Общая форма объявления одиночного лямбда-выражения, которое принимает один параметр:
- параметр – параметр, который получает на входе лямбда-выражение;
- выражение – непосредственно выражение, которое вычисляется.
Общая форма объявления одиночного лямбда-выражения, которое принимает несколько параметров:
- список_параметров – два и более параметра, которые используются в лямбда-выражении.
Пример 1 одиночного лямбда-выражения, которое вычисляет значение cos(x+2) :
В данном примере x есть входным параметром. Данное выражение заменяет функцию приблизительно следующего вида:
Как видно из программного кода, использование лямбда-выражения упрощает программный код, в особенности в случаях, если нужно вызывать методы которые выполняют разную работу но имеют одинаковую сигнатуру.
Пример 2 одиночного лямбда-выражения, которое получает 3 параметра с именами a , b , c . В данном примере определяется, можно ли из длин сторон a , b , c образовать треугольник?
В вышеприведенном лямбда-выражении используется правило: сумма двух любых длин сторон треугольника больше длины третьей стороны.
⇑
6. Какая общая форма объявления блочного лямбда-выражения? Пример
Общая форма объявления блочного лямбда выражения, получающего один параметр:
Общая форма объявления блочного лямбда-выражения, получающего несколько параметров:
Пример.
В примере объявлено лямбда-выражение, которое получает параметром целое число x . В коде лямбда-выражения делается подсчет количества цифр ‘5’ в целом числе x . Например, для числа 5854 лямбда-выражение возвращает значение 2.
Возвращение значения осуществляется оператором return .
⇑
7. Какие действия (шаги) нужно выполнить, чтобы в программе применить лямбда-выражение? Пример
Чтобы в программном коде применить лямбда-выражение, нужно выполнить следующую последовательность действий:
- Объявить тип делегата, совместный с лямбда-выражением.
- Объявить переменную этого типа делегата (экземпляр делегата).
- Присвоить переменной (экземпляру делегата) лямбда-выражение.
- Вызвать переменную (экземпляр делегата) в программном коде.
Пример. Пройдем все шаги последовательно для задачи из предшествующего пункта (п.6). Пусть нужно продемонстрировать работу лямбда-выражения, которое получает входным параметром целое число x и вычисляет количество цифр ‘5’ в этом числе.
1. Объявить тип делегата. Тип делегата объявляется в некотором классе.
Имя типа делегата CalcNum5. Делегат этого типа будет получать один входной параметр ( x ). Делегат этого типа возвращает значение типа int .
2. Объявить переменную этого типа делегата. Переменная объявляется в некотором программном коде. Это может быть код произвольного метода класса, код обработчика события и т.п.
3. Присвоить переменной лямбда-выражение. В методе объявления переменной объявляется присваивание:
4. Вызвать переменную. В том же методе где объявлено лямбда-выражение реализован вызов переменной. Имя CN – это имя делегата, который содержит лямбда-выражение.
⇑
8. Пример использования одиночного лямбда выражения, получающего один параметр
В данном примере продемонстрировано использование лямбда-выражения, в котором вычисляется функция y = sin²x .
Сначала объявляется тип делегата, который получает один параметр типа float и возвращает значение типа float . Параметр имеет название – x . В обработчике события button1_Click () продемонстрировано использование лямбда-выражения.
⇑
9. Пример использования одиночного лямбда-выражения, которое получает несколько параметров
В данном примере, для заданных x , y вычисляется функция z = sin x² · cos y .
Чтобы объявить лямбда-выражение, сначала нужно объявить тип делегата, который получает 2 параметра и возвращает значение. Тип параметров и значения – double .
Объявление типа делегата имеет вид:
Демонстрация использования лямбда-выражения из другого метода (например, обработчика события)
⇑
10. Пример блочного лямбда-выражения, которое получает несколько параметров
Задача
Дано три разных целых числа. Реализовать лямбда-выражение, которое находит наибольшее из этих трех чисел.
Решение
Сначала объявляется тип делегата, который получает три параметра целого типа и возвращает целое значение
Затем, в другом программном коде, можно объявить лямбда-выражение и продемонстрировать его работу
⇑
11. Можно ли в одном методе реализовать различные лямбда-выражения, которые соответствуют одному типу делегата?
Пример.
Пусть объявлен тип делегата, получающий три параметра целого типа.
Тогда, в обработчике события или в другом методе можно написать так:
- находят максимальное значение;
- находят сумму параметров;
- находят произведение параметров.
В Python функцию можно вызывать саму из себя. Это называется рекурсией. В качестве примера рекурсивной функции я приведу вычисление числа в степени n (n – целое число).
И, далее, можно ее вызвать так:
Теперь подробнее разберемся как она работает. Для начала заметим, что
то есть, для вычисления значения на текущем n-м шаге достаточно взять значение на предыдущем n-1-м шаге и умножить его на x. Эта формула, по сути, отражает принцип рекурсии. В нашем случае она будет выглядеть так:
Далее, запуск функции осуществляется с аргументами 2 и 3: pow(2, 3). Она помещается в стек вызова функций, в котором хранится порядок вызова различных функций. Далее, выполняется тело функции. Проверяется первое условие. Оно оказывается ложным, так как 3 == 0 дает false. Поэтому идет переход на else и прежде чем выполнить оператор return, снова вызывается та же функция pow(2, 2).
Выполнение функции pow(2, 3) останавливается, в стек помещается вторая функция pow(2, 2) и она запускается. Снова проверяется первое условие, оно ложно, переходим по else к оператору return и опять вызываем функцию pow(2, 1).
Здесь снова все повторяется, в стек помещается очередной вызов функции и по условию вызывается следующая функция pow(2, 0).
Теперь первое условие становится истинным и функция pow(2,0) возвращает значение 1 и рекурсия не идет дальше вглубь – она останавливается. Функция pow(2.0) завершается, она удаляется из стека вызовов и управление передается функции pow(2, 1). Но она частично уже выполнена. Поэтому, берется значение 1 от pow(2, 0), результат умножается на x=2 и величина 2 возвращается функцией pow(2, 1).
Функция pow(2,1) также удаляется из стека, управление переходит к вышестоящей функции pow(2,2) и здесь мы уже имеем результат умножения x*x, то есть, 2*2 = 4. Далее, возвращаемся к самой верхней функции pow(2,3), здесь 2*4=8 и этот результат становится результатом вычисления рекурсивной функции.
Функции с произвольным числом аргументов
Для начала вспомним, что когда мы говорили о кортежах, то указывали на такую работу оператора присваивания:
Здесь x и y принимают значения 1 и 2 соответственно. Но когда этих значений было больше, чем переменных:
то возникала ошибка. Так вот, смотрите, если поставить оператор *, например, перед y, то ошибки не возникнет:
и переменная x = 1, а y = [2, 3, 4]. То есть, во вторую переменную будут записаны все оставшиеся значения в виде списка. Или же, можно сделать так:
Тогда первые три значения будут помещены в x:
а последнее в y: y = 4. То же самое работает и со списками:
И вообще с любыми перечисляемыми типами данных. То есть, оператор * упаковывает оставшиеся значения в список. Правда, мы не можем упаковывать уже упакованные данные, например, так:
произойдет ошибка, но вот так:
Этот же оператор может выполнять и обратную операцию – распаковывать списки в набор данных. Смотрите, предположим, мы описываем, что счетчик в цикле for должен пробегать диапазон от -5 до 5 с шагом 1. Как вы уже знаете, это делается так:
Далее, мы хотим представить этот диапазон с помощью списка:
и передать его функции range. Если записать просто a:
То возникнет ошибка, т.к. функция ожидает числа, а не список. Но, записав * перед переменной a, произойдет распаковка списка в набор из двух чисел: -5 и 6:
- Если выполняется присвоение данных переменной с оператором *, то данные упаковываются. Например, *y, x = 1,2,3.
- Если выполняется передача списка с указанием оператора *, то происходит его распаковка. Например, range(*[-5,6]).
Этот оператор можно использовать для объявления функций с произвольным числом аргументов:
Смотрите, у нас args становится кортежем с переданными значениями при вызове функции. Значит, мы спокойно можем перебрать все значения кортежа, например, в цикле for:
В результате получили функцию с произвольным числом аргументов. Где это может пригодиться? Например, реализовать универсальную функцию для вычисления суммы переданных элементов:
А что если мы хотим передавать функции неограниченное количество именованных аргументов? Вот так:
В этом случае аргумент у функции следует записать с двумя звездочками:
И при ее вызове мы видим, что kwargs – это словарь. Как перебирать элементы словаря мы уже знаем, например, можно сделать так:
и далее, выполняем определенные действия в зависимости от значений именованных аргументов.
Но, если мы теперь попытаемся вызвать функцию вот так:
то возникнет ошибка, т.к. первые три аргумента не именованные. Для такого варианта вызовов функцию myFunc следует записать так:
Причем, вот эти неименованные аргументы должны всегда следовать перед именованными. Или же можно сделать так:
Все эти комбинации вполне допустимы, главное чтобы их порядок был именно таким: сначала фактические параметры, затем необязательный список args, затем формальные параметры и потом список этих параметров.
Анонимные или лямбда-функции
В Python существуют так называемые анонимные функции (они же лямбда-функции). Что это такое? Давайте для начала рассмотрим такой пример:
У нас имеется функция showElements, которая отображает из списка lst только те элементы, для которых функция func возвращает значение True. Далее, мы создаем вспомогательную функцию __odd, которая возвращает True для нечетных значений. И идет вызов showElements для списка a и селектора в виде функции __odd.
Но что если мы захотим изменить селектор и выбирать, например, все четные значения? Нужно создавать новую функцию и затем указывать ссылку на нее? Это как то не очень удобно. Вот здесь нам на помощь приходят лямбда-функции. Их можно объявить в любом месте программы по следующему синтаксису:
lambda arg1, arg2, …: выражение
и, затем, вызвать ее:
То есть, в анонимных функциях можно писать после двоеточий любой оператор, но только один. Несколько нельзя. И кроме того, результат работы этого оператора автоматически возвращается лямбда-функцией.
В нашем случае мы можем записать такую функцию сразу в качестве второго аргумента:
Видите, как это удобно, понятно и наглядно. Мы сразу видим как будет выбирать значения функция showElements.
Если анонимная функция не принимает никаких аргументов, то она записывается так:
Вот такие интересные и полезные возможности в объявлении функций существуют в языке Python.
Задания для самоподготовки
1. Написать рекурсивную функцию для вычисления факториала числа n:
2. Написать функцию для вычисления среднего арифметического переданных ей значений в виде аргументов:
arg1, arg2, …, argN
3. Реализовать функцию сортировки выбранных элементов по возрастанию: элементы передаются функции в виде списка и выбираются они с помощью функции-селектора, указанной в качестве второго параметра. Привести примеры вызова функции сортировки с разными видами селекторов. Селекторы реализовать в виде лямбда-функций.
Видео по теме
© 2022 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Читайте также: