Зачем передавать функцию в качестве аргумента другой функции, если мы можем просто вызвать ее для реализации? - PullRequest
0 голосов
/ 23 января 2019

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

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

func add(_ a : Int, _ b : Int) -> Int {
    return a + b 
}

при вызове функции из другой функции

func average(_ a : Int, _ b : Int) -> Int{
    return add(a, b) / 2
}

при передаче функции в качестве аргумента другой функции

func averageArg(_ plus: (Int, Int) -> Int, _ a: Int, _ b : Int) -> Int {
    return plus(a, b) / 2
}

Ответы [ 4 ]

0 голосов
/ 23 января 2019

Это особенность swift, которая используется не часто, но время от времени может оказаться чрезвычайно полезной.В вашем примере это действительно не имеет смысла, но давайте найдем сценарий, в котором он действительно имеет смысл.

Давайте сначала посмотрим, что означает _ plus: (Int, Int) -> Int.Этот фрагмент кода означает, что ваша функция averageArg принимает любую замыкание или функцию , которая принимает два целочисленных параметра и предоставляет целое число в качестве вывода.Поскольку add(...) соответствует этому требованию (два integer аргумента и он дает integer в качестве вывода), вы можете передать его в качестве аргумента.

Но давайте рассмотрим пример, где такой параметр имел бы смысл.Допустим, мы пишем код переднего плана для приложения, которое показывает отзывы об отеле.В этом приложении нам нужно где-то написать функцию fetchReviews(_ serverResponse: ([Review]) -> Void), которая получает все отзывы об отеле с какого-либо удаленного сервера:

struct Review {
    let author: String
    let rating: Int
}

func fetchReviews(_ completionHandler: @escaping ([Review]) -> Void) {
    // Lets do a request to the server on a separate thread, so that the user can keep
    // interacting with the app. The app doesn't freeze.
    // Making qos .userInitiated will ensure that user interaction will be more important than
    // our backend request.
    DispatchQueue.global(qos: .userInitiated).async {
        // Do some request to the server....

        //...

        // Lets pretend we have received a response, and we are turning the response into an array of reviews
        completionHandler([Review]())
    }
}

Поскольку между ответом сервера и его выдачей может быть (иногда длительная) задержкавсе обзоры, а также между тем, когда fetchReviews был вызван, для пользователя было бы нехорошо, если бы приложение просто зависло.Вместо этого вы можете выполнить запрос к серверу в отдельном потоке, чтобы пользователь мог продолжать использовать приложение, а приложение не зависало (см. Часть DispatchQueue.global).

Однако мы все равно хотим отображать все отзывы пользователю, как только мы получим ответ от сервера.Добавив параметр _ serverResponse, мы можем уведомить любого, кто вызвал fetchReviews, как только мы действительно получим ответ сервера.Делая так, пользователь может продолжать взаимодействовать с приложением, и как только обзоры станут доступны, мы сможем показать их пользователю!

0 голосов
/ 23 января 2019

Причина использования функции в качестве аргумента состоит в том, что она делает вашу функцию более гибкой. Это позволяет вызывающей стороне решить, что делает plus, передав функцию или замыкание, которое объединяет два значения Int для возврата значения Int.

Например, предположим, что вызывающая сторона хочет, чтобы plus умножил значения:

print(averageArg(*, 5, 6))
15

или взять max из двух значений (передав закрытие):

print(averageArg({ max($0, $1) }, 1, 100))
50

Лучшим примером этого является функция Swift sorted(by:). sorted принимает закрытие, которое определяет, что означает areInIncreasingOrder. Это позволяет сортировать массив по возрастанию и убыванию, просто изменив переданную функцию:

[1, 3, 2, 4].sorted(by: <)
[1, 2, 3, 4]
[1, 3, 2, 4].sorted(by: >)
[4, 3, 2, 1]

Вот пример использования функции:

func shortNamesFirst(_ name1: String, _ name2: String) -> Bool {
    if name1.count < name2.count {
        return true
    } else if name1.count > name2.count {
        return false
    } else {
        // when names are the same length, sort
        // them alphabetically
        return name1 < name2
    }
}

["Chuck", "Bob", "Bill", "Jo", "Ed", "Alexander"].sorted(by: shortNamesFirst)
["Ed", "Jo", "Bob", "Bill", "Chuck", "Alexander"]

Автор sorted не обязан предоставлять другую версию sorted для каждого типа заказа, который может пожелать пользователь. Пользователь может решить, что для него значит sorted.

0 голосов
/ 23 января 2019

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

func doSomething(with: AnyObject, whenFails failureClosure: ((Error) -> Void)? = nil) {
  var error: Error?

  // do stuff, maybe setting the error to something...

  if let error = error,
     let failureClosure = failureClosure {
         failureClosure(error)
  }
}

Это позволяет вызывающей стороне doSomething, но обрабатывать сбои функции пользовательским способом, передавая замыкание.

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

func doSomethingAsynchronously(completion completionClosure: (() -> Void)? = nil) {
   // Do asynchronous stuff

   if let completionClosure = completionClosure {
      completionClosure()
   }
}
0 голосов
/ 23 января 2019

Например, без функции «postProcess» в качестве аргумента вам нужно будет реализовать функции дерева: getAndAddData, getAndSubData и getAndMulData.

func add(_ a:Int, _ b:Int) -> Int {
    return a + b
}

func sub(_ a:Int, _  b:Int) -> Int{
    return a - b
}

func mul(_ a:Int, _  b:Int) -> Int{
    return a * b
}

func getAndProcessData(_ postProcess:(Int, Int) -> Int, _ a:Int, _ b:Int) -> Int {

    let a2 = ExampleClass.get(a)
    let b2 = ExampleClass.get(b)
    return postProcess(a2, b2)
}

func exampleFunc(a:Int, b:Int) {

    let getAndAdd = self.getAndProcessData(self.add(_:_:), a, b)
    let getAndSub = self.getAndProcessData(self.sub(_:_:), a, b)
    let getAndMul = self.getAndProcessData(self.mul(_:_:), a, b)
}
...