Поясните логику функции

swift

#1

Подскажите начинающему, есть такая функция с замыканием и захватом значения

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
     var runningTotal = 0
     func incrementer() -> Int {
          runningTotal += amount
          return runningTotal
     }
     return incrementer
}


let incrementByTen = makeIncrement(forIncrement: 10)

incrementByTen()
// возвращает 10
incrementByTen()
// возвращает 20
incrementByTen()
// возвращает 30

Затем передаем второй аргумент и сохраняем результат в новой константе

let incrementBySeven = makeIncrementer(forIncrement: 7)

incrementBySeven()
// Возвращает 7
incrementBySeven()
// Возвращает 14

Теперь снова используем предыдущую константу / функцию и с аргументом 10

incrementByTen()
// возвращает 40

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

Где хранятся результаты таких вызовов?


#2

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

но когда есть замыкания, то scope не удаляется пока вложенные функции не перестанут использоваться.

в твоем случае вложенная функция перестанет использоваться тогда, когда на нее не останется ссылок. incrementByTen - ссылка на функцию, а функция использует один из scope’ов makeIncrementer. если сделать incrementByTen = nil, то по идее и вложенная функция, и используемый ею scope должны удалиться.

если сделать вот так makeIncrement(forIncrement: 10)() то оно вернет 0 и сразу все удалит, так как никаких ссылок не осталось.

не знаю как оно на самом деле работает в свифте на низком уровне, но в теории именно так и должно быть.


#3

Сэнкс, это то, что надо.

Единственный вопрос - где хранятся копии этих scope?


#4

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

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


#5

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

И второй момент, что само хранение наверное делается где-то в этих новых константах со ссылкой на функцию. Потому что реально меняются только они, весь остальной путь одинаков.

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


#6

Скажи, пожалуйста, а что это за вызов функции - makeIncrement(forIncrement: 10)()

Почему здесь в конце еще одни круглые скобки?


#7
let incrementByTen = makeIncrement(forIncrement: 10)

incrementByTen()

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


#8

спасибо, что подсказываешь))

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

Т.е. вроде как должно быть так

makeIncrement(forIncrement: 10)

А ты вызываешь так

makeIncrement(forIncrement: 10) ( )

И оно работает, да. Я как бы догадываюсь, что это тоже вызов функции, но не совсем его понимаю.

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

В общем, поясни, плз, что-то не въезжаю


#9

12345678901234567890


#10

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

И насчет операндов и операторов. Правильно ли будет так сказать, что это операнд?

makeIncrement(forIncrement: 10)

и что за оператор ( ) Что он делает?


#11

12345678901234567890


#12

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

И кстати, такой же вариант с этой функцией проходит только частично, например,

makeIncrement (forIncrement: 10) ( )

проходит нормально и выдает 10. А если добавить еще один оператор ( )

makeIncrement (forIncrement: 10) ( ) ( )

то не пускает, выдает ошибку cannot call value of non-function type 'Int’
makeIncrementer(forIncrement: 10)()()

В общем, тут уже наверное стоит остановиться, потому что в этих замыканиях всего много наворочено.

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


#13

12345678901234567890


#14

пипец))

В общем понял, что мне еще есть куда расти))

Тему закрываем, спасибо за помощь