Assigning high-order functions

I am learning about high order functions in Swift (like .map.filter.reduce ...) and generic types.

Here is my function:

func max<T: Comparable>(_ array: [T]) -> T {
    var max = 0 as! T

    for value in array {
        if value > max { max = value }
    }

    return max
}

      

How can I replace the for loop with a higher order function to get the same result?

I would like to do something like this (or better):

max = array.map { $0 > max ? $0 : max }

      

+3


source to share


3 answers


First of all, note that your approach to "seed" and forced to cast

var max = 0 as! T

      

has two problems:

  • It will split into non-integer arrays for example. max(["a", "b"]

    ).
  • Even for whole arrays it is not true if all elements of the array are negative, eg. max([-2, -3])

    must be -2

    and not equal to zero.

So, it is better to choose the first element of the array as the initial "forced zero" value.

This leads to the next question: what if the array is empty? There are two valid approaches, you can require the function to be called with a non-empty array (and a document, which is the premise):

/// Compute the maximal element in an array.
///
/// - Returns: The maximal element.
///
/// - Note: The array must not be empty.
func max<T: Comparable>(_ array: [T]) -> T {
    precondition(!array.isEmpty, "`max` called with empty array")

    var max = array[0]
    for value in array {
        if value > max { max = value }
    }
    return max
}

      

Or (as also suggested in other answers) make the return value optional:

/// Compute the maximal element in an array.
///
/// - Returns: `nil` if the array is empty, and the maximal element otherwise.
func max<T: Comparable>(_ array: [T]) -> T? {

    guard var max = array.first else { return nil }
    for value in array {
        if value > max { max = value }
    }
    return max
}

      

Both approaches can be accomplished with reduce()

. The first will be



/// Compute the maximal element in an array.
///
/// - Returns: The maximal element.
///
/// - Note: The array must not be empty.
func max<T: Comparable>(_ array: [T]) -> T {
    precondition(!array.isEmpty, "`max` called with empty array")
    return array.reduce(array[0]) { $0 > $1 ? $0 : $1 }
}

      

and second

/// Compute the maximal element in an array.
///
/// - Returns: `nil` if the array is empty, and the maximal element otherwise.
func max<T: Comparable>(_ array: [T]) -> T? {
    guard let first = array.first else { return nil }
    return array.reduce(first) { $0 > $1 ? $0 : $1 }
}

      

This can be further shortened with the method flatMap()

Optional

:

/// Compute the maximal element in an array.
///
/// - Returns: `nil` if the array is empty, and the maximal element otherwise.
func max<T: Comparable>(_ array: [T]) -> T? {
    return array.first.flatMap { array.reduce($0) { $0 > $1 ? $0 : $1 } }
}

      

Finally, you can use existing

func max<T : Comparable>(_ x: T, _ y: T) -> T

      

instead of literal closure in all of the above examples, eg.

/// Compute the maximal element in an array.
///
/// - Returns: `nil` if the array is empty, and the maximal element otherwise.
func max<T: Comparable>(_ array: [T]) -> T? {
    return array.first.flatMap { array.reduce($0, max) }
}

      

+1


source


Decrease!

return array.reduce(nil)
{
    (max: T?, current: T) -> T? in
    guard let max = max else { return current }
    return max > current ? max : current
}

      

This will return optional, but it's probably sane if you step through an empty array.

Of course there is this one

https://developer.apple.com/documentation/swift/array/1688806-max

This is an instructional exercise. So, here is a generalization of the solution that uses higher order functions. Note that there is already a function in the Swft Strandard library that does this.



extension Array 
{
     func pickOne(choose: (Element, Element) -> Element) -> Element?
     {
         return self.reduce(nil)
         {
             (bestSoFar: Element?, current: Element) -> Element? in
             guard let bestSoFar = bestSoFar else { return current }
             return choose(bestSoFar, current)
         }
     }
}

      

So now the max functionality is defined like this:

array.pickOne { $0 > $1 ? $0 : $1 }

      

and min will be

array.pickOne { $0 < $1 ? $0 : $1 }

      

+3


source


For Ints, you would like to use reduce

it like this:

// Reduce with initial value the first value of the array if available,
// or 0 otherwise
let max = array.reduce(array.first ?? 0) { (max, newValue) -> T in
    return newValue > max ? newValue : max 
}

      

UPDATE

You want JeremyP's answer for handling the whole Comparable correctly!

+1


source







All Articles