你如何使用闭包在一个整数数组中查找最小和最大元素?

huangapple go评论51阅读模式
英文:

How do I find min and max elements in an array of Int using closures?

问题

Here's the translated code portion:

我有一个任务要创建一个带有数组和接受Int和Int?并返回Bool的闭包的函数。然后,我需要使用闭包找到数组的最小和最大元素。

这是我做的事情:

```swift
func task(array: [Int], closure: (Int, Int?) -> Bool) -> Int? {
    var a: Int?  // 这是任务的一部分 - 创建一个可选变量
    
    for i in array {
        if closure(i, a) {
            a = i
        }
    }
    return a
}


var numbers = [1, 2, 3, 46, 6, 2, 5, 7]

但是我不明白如何使用它来找到最小值和最大值,我应该在闭包中放什么?

我看到有人这样做:

let max = task(array: numbers) {
    $1 == nil || $1! > $0
}

let min = task(array: numbers) {
    $1 == nil || $1! < $0
}

但我仍然不明白他们为什么要比较$0和$1!,以及为什么我们需要两个元素来找到一个最小/最大值?你能解释一下逻辑吗?


Please let me know if you need any further assistance.

<details>
<summary>英文:</summary>

I have a task to make a func with an array and a closure that accepts Int and Int? and returns Bool. Then, I have to find min and max elements of the array using the closure.

Here&#39;s what I&#39;ve done:

func task(array: [Int], closure: (Int, Int?) -> Bool) -> Int? {
var a: Int? // it's a part of the task - to make an optional variable

for i in array {
    if closure(i, a) {
        a = i
    }
}
return a

}

var numbers = [1, 2, 3, 46, 6, 2, 5, 7]


But I don&#39;t understand how to find min and max using it, what should I put in the closure?

I saw someone did this:


let max = task(array: numbers) {
$1 == nil || $1! > $0
}

let min = task(array: numbers) {
$1 == nil || $1! < $0
}


But I still can&#39;t get why did they compare $0 to $1! and why do we need two elements to find one min/max? Can you please explain the logic? 


</details>


# 答案1
**得分**: 2

The `task` 函数迭代每个数字并使用闭包来判断每个数字是否比先前找到的“更好”。由于一开始没有数字,`resultSoFar` 最初为 `nil`。这就是为什么需要向闭包传递一个整数和一个可选整数。第一个是来自数组的候选数字,第二个是到目前为止找到的最佳结果。`min` 和 `max` 函数需要在候选数字比较小或大于迄今为止的数字时返回 `true`,以便最终结果将是所有数字中最小/最大的。如果迄今为止的结果是 `nil`,那么这是第一个候选数字,必须是迄今为止的最小/最大值。

你的同学之所以弄反了最小值和最大值,是因为他们错误地比较了迄今为止的结果和候选数字。

最好避免使用强制解包(`!`),所以稍微改进一下使用 `task` 的方式会是这样:

```swift
let max = task(numbers: numbers) { number, resultSoFar in
    guard let resultSoFar else { return true }
    return number > resultSoFar
}

let min = task(numbers: numbers) { number, resultSoFar in
    guard let resultSoFar else { return true }
    return number < resultSoFar
}

if let min = min, let max = max {
    print("min: \(min), max: \(max)")
} else {
    print("数组为空,没有最小或最大数字")
}

为了“看到”任务的工作方式,可以添加一些调试打印语句。这应该会清楚地显示了 resultSoFar 的使用方式。

在计算最大值时,调试打印如下:

Before checking 1, resultSoFar is nil
Setting resultSoFar to 1
Before checking 2, resultSoFar is Optional(1)
Setting resultSoFar to 2
Before checking 3, resultSoFar is Optional(2)
Setting resultSoFar to 3
Before checking 46, resultSoFar is Optional(3)
Setting resultSoFar to 46
Before checking 6, resultSoFar is Optional(46)
Before checking 2, resultSoFar is Optional(46)
Before checking 5, resultSoFar is Optional(46)
Before checking 7, resultSoFar is Optional(46)
Returning Optional(46)
英文:

The task is deliberately a bit strange to teach you about closures, I think. Before fully explaining what is happening, look at this version of the code with different variable names. Try and understand what is happening before reading further.

func task(numbers: [Int], compare: (Int, Int?) -&gt; Bool) -&gt; Int? {
    var resultSoFar: Int?  // this is initially nil
    
    for number in numbers {
        if compare(number, resultSoFar) {
            resultSoFar = number
        }
    }
    return resultSoFar
}

let numbers = [1, 2, 3, 46, 6, 2, 5, 7]

let max = task(numbers: numbers) { number, resultSoFar in
    resultSoFar == nil || number &gt; resultSoFar!
}

let min = task(numbers: numbers) { number, resultSoFar in
    resultSoFar == nil || number &lt; resultSoFar!
}

print(&quot;min: \(min), max: \(max)&quot;)
// &quot;min: Optional(1), max: Optional(46)&quot;

The task function iterates over each of the numbers and uses the closure to see if each number is a “better” solution than whatever number it had previously found. Since there is no number to start with, resultSoFar is initially nil. That is why the closure needs to be passed an integer and an optional integer. The first is the candidate number from the array, the second is the best result so far. The min and max functions need to return true if the candidate number is smaller or larger than the number so far so that the final result will be the smallest/largest of all the numbers. If the result so far is nil then this is the first candidate number and must be the smallest/largest so far.

Your classmate got their min/max the wrong way round because they are comparing the result so far and the candidate number the wrong way round.

Using force unwrapping (!) is best avoided so a slightly better way of using task would look like this:


let max = task(numbers: numbers) { number, resultSoFar in
    guard let resultSoFar else { return true }
    return number &gt; resultSoFar
}

let min = task(numbers: numbers) { number, resultSoFar in
    guard let resultSoFar else { return true }
    return number &lt; resultSoFar
}

if let min = min, let max = max {
    print(&quot;min: \(min), max: \(max)&quot;)
} else {
    print(&quot;there is no minimum or maximum number in an empty array&quot;)
}

To “see” the way the task works some debug prints can be added. This should make it really clear how resultSoFar is used:

func task(numbers: [Int], compare: (Int, Int?) -&gt; Bool) -&gt; Int? {
    var resultSoFar: Int?  // this is initially nil
    
    for number in numbers {
        print(&quot;Before checking \(number), resultSoFar is \(resultSoFar)&quot;)
        if compare(number, resultSoFar) {
            print(&quot;Setting resultSoFar to \(number)&quot;)
            resultSoFar = number
        }
    }

    print(&quot;Returning \(resultSoFar)&quot;)
    return resultSoFar
}

When calculating the maximum the debug prints look like this:

Before checking 1, resultSoFar is nil
Setting resultSoFar to 1
Before checking 2, resultSoFar is Optional(1)
Setting resultSoFar to 2
Before checking 3, resultSoFar is Optional(2)
Setting resultSoFar to 3
Before checking 46, resultSoFar is Optional(3)
Setting resultSoFar to 46
Before checking 6, resultSoFar is Optional(46)
Before checking 2, resultSoFar is Optional(46)
Before checking 5, resultSoFar is Optional(46)
Before checking 7, resultSoFar is Optional(46)
Returning Optional(46)

答案2

得分: 1

以下是翻译好的部分:

你已经在Geoff Hackworth的答案中得到了一个很好的解释和良好的解决方案。闭包的第二个参数是迄今为止找到的“最佳”数字,对于闭包的第一次调用来说是nil

为了完整起见:还可以使用Optional.map和可选链接 ?? 来实现这个目的:

let max = task(array: numbers) { number, resultSoFar in
    resultSoFar.map { number > $0 } ?? true
}
  • 如果resultSoFarnil,那么返回true

  • 否则,使用$0作为resultSoFar解包值来调用闭包{ number > $0 },并返回比较的结果。

task函数本身可以使用reduce来简化:

func task(array: [Int], closure: (Int, Int?) -> Bool) -> Int? {
    return array.reduce(nil) { closure($1, $0) ? $1 : $0 }
}
英文:

You already got an excellent explanation and good solutions in Geoff Hackworth's answer. The second argument of the closure is the “best” number found so far, and that is nil for the first call of the closure.

For the sake of completeness: One can also use Optional.map and optional chaining ?? for this purpose:

let max = task(array: numbers) { number, resultSoFar in
    resultSoFar.map { number &gt; $0 } ?? true
}
  • If resultSoFar is nil then true is returned.

  • Otherwise the closure { number &gt; $0 } is called with $0 being the unwrapped value of resultSoFar and the result of that comparison is returned.

The task function itself can be simplified using reduce:

func task(array: [Int], closure: (Int, Int?) -&gt; Bool) -&gt; Int? {
    return array.reduce(nil) { closure($1, $0) ? $1 : $0 }
}

huangapple
  • 本文由 发表于 2023年4月13日 19:41:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76004999.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定