使用函数式编程的find()函数

5
我希望创建一个通用的find()函数,这通常在函数式编程中使用。在函数式编程中,您不使用数组索引和for循环,而是使用过滤器。它的工作原理是如果您有一个列表,比如说:

["apple", "banana", "cherry"] 

如果你想要查找香蕉,那么你可以通过创建元组将数组索引分配给列表元素。
[(1, "apple"), (2, "banana"), (3, "cherry")] 

现在你可以筛选到“banana”并返回索引值。
我试图创建一个通用的函数,但是我遇到了一个错误。这个语法有什么问题?
func findInGenericIndexedList<T>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int? {        
    let found = indexedList.filter {   // ERROR: Cannot invoke 'filter' with an argument list of type '((_) -> _)'

        element.value === $0.value
    }

    if let definiteFound = found.first {
        return definiteFound.index
    }
    return nil
}
更新1: 我想使用上面的解决方案,而不是使用find()(将被弃用)或在Swift 2.0中使用indexOf(),因为我正在尝试遵循函数式编程范例,依赖于通用函数而非类方法。

  1. element === $0.value 中,您将类型为 (Int, T) 的元组与类型为 T 的值进行比较。
  2. ===,即“恒等于”运算符,仅适用于的实例。
  3. 您是否知道 Swift 已经有一个 find 函数?(在 Swift 2 中它被称为 indexOf。)
- Martin R
1
谢谢你的建议!我更新了我的问题,解释了为什么find()和indexOf()不是我首选的解决方案。我根据你的第一点更新了代码。这很有道理,但它仍然无法正常工作。如果'==='身份运算符在这种情况下无法工作,那么你会如何编写这样一个通用函数呢? - Daniel
2个回答

2

使其正常工作所需的最小更改是使 T 符合 Equatable 并使用 == 运算符。

func findInGenericIndexedList<T:Equatable>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int? {
    let found = indexedList.filter {
        element.value == $0.value
    }

    if let definiteFound = found.first {
        return definiteFound.index
    }
    return nil
}

在这里使用 === 并不是很合理,因为通常会将其应用于值类型(特别是如果您遵循函数式编程范例),而这种情况下它永远不会成立。

除此之外,我花了一些时间思考这个问题,以下是我的解决方案:

extension Array where Element : Equatable {
    func find(element:Array.Generator.Element) -> Int? {
        let indexedList = lazy(self.enumerate())
        let found = indexedList.filter {
            element == $1
        }

        let definiteFound = found.prefix(1)
        return definiteFound.generate().next()?.index
    }
}

在数组上进行协议扩展是因为它使语法更加简洁,使用惰性序列可以避免检查每个元素,索引从0开始。


感谢您详细的回答。我仍然更喜欢使用身份运算符“===”,因为在我的数组中可能有多个相同值的项。但是,我想删除我传递给函数的确切项。这是否不可能?(我真的很喜欢您的扩展程序。它可能不太像函数式编程,而更像面向对象编程。FP更多地使用高阶函数。一切都被传递。) - Daniel
你如何定义“确切的项”?它是具有相同索引和相同内容的项吗?也就是说,(Int,String)元组将是相同的吗?或者您预计会有多个完全相同的元组? - Aaron Rasmussen
@Roman 显然你不会已经知道索引。否则你可以直接返回它。 - Tristan Burnside
@Tristan:在他编写的示例函数中,他传递了一个元素,该元素是包含索引和值的元组。我觉得整个练习有点奇怪,因为我不确定他为什么要为数组中的项目创建(index, value)元组,但这就是他所拥有的。一个(index, value)元组的数组,他正在寻找与(index, value)匹配的元组。因此,他似乎认为在搜索之前就会知道索引。我知道。我也不明白。我的评论意图是让他澄清一下…… - Aaron Rasmussen
@Tristan,我不是在寻找元组。我在寻找元素(元组中的第二个变量)并返回相应的索引。这就是您在函数式编程中查找索引的方式(请参见帖子开头)。重要的是我知道我正在使用的元素--我有一个对它的引用。现在,我想要它在数组中的索引。因此,不想比较值,因为(a)我可能会有重复,并且(b)因为元素(元组中的第2个变量)实际上可能是具有许多复杂属性的复杂结构。如何在通用函数中实现此操作? - Daniel
@Daniel,通过使T符合AnyObject而不是Equatable,可以使用类来完成此操作,但将函数限制为对象并不是非常实用。如果您有复杂的structs,则应该将它们制作成由几个小structs组成的结构体,每个内部structs都是可比较的,然后您可以比较每个内部structs进行比较。这意味着在任何级别上,检查都相对简单。 - Tristan Burnside

1

以下是一些想法。

我仍然更喜欢使用恒等运算符“===”,因为在我的数组中可能有多个相同值的项。

恒等运算符 === 仅适用于引用类型,如类。它永远不会适用于值类型,如 String Int struct 等。您可以查看值类型和引用类型之间的区别,特别是如果您对函数式编程感兴趣,该编程几乎完全避免使用引用类型。当您使用值类型时,只有相等性( == )-没有身份。两个 String 实例“ bananas ”永远不会指向相同的对象。它们总是引用两个不同的 String ,尽管它们的值可能是相等的

我想删除我传递给函数的确切项。这是不可能的吗?

如果你使用值类型,例如字符串,那么是不可能的。不存在两个完全相同的字符串。由于上述原因,两个字符串始终是不同的对象。
请注意,如果您只使用类而不使用值类型,则可以使用“===”运算符,但这将破坏您尝试做的大部分事情。
归根结底,如果您有一个包含以下内容的<(索引,值)>元组数组:
[(0, "bananas"), (1, "apples"), (2, "oranges"), (3, "bananas")]

如果你编写一个函数来查找值为"bananas"的元组,你有几个选择。你可以过滤它并查找数组中第一个具有"bananas"值的元组,并返回该元组的索引。在上面的例子中,它将返回0。或者,你可以以数组形式返回所有索引,如下所示:[0, 3]。或者我想你可以返回其他任意一部分结果,比如最后一个索引,或者第一个和最后一个索引等等,但这些都似乎有点傻。Swift标准库选择返回与搜索条件匹配的第一项的索引,正是出于这个原因。没有其他选项有太多意义。
但是,将其放回到你的问题的背景中,你找到的所有值为"bananas"的元组都不会是你传递给搜索函数的确切(相同)实例"bananas"。没有两种值类型是完全相同的。它们可能相等,但永远不会相同。

还有一点需要说明,主要是为了澄清您正在尝试做什么。在您第一次尝试编写此函数时,您似乎已经知道要搜索的项目的索引。您将其作为参数传递给函数,在这里:

                                                                     // ----------vvvvv
func findInGenericIndexedList<T>(indexedList: [(index: Int, value: T)], element: (index: Int, value: T)) -> Int?

只是出于好奇,这是打字错误吗?还是你实际上知道正在搜索的元组的索引?因为如果你已经知道了它是什么,那么...你就不需要搜索它了 :)


是的,这是一个笔误。 :) 我应该只传递元素。再次感谢您详细的回复。我很惊讶您无法查找相同的引用。原始类型、字符串、复杂类型,它们都有对它们的引用。即使是不可变的。我接受您不能这样做,但我仍然不明白为什么。为什么我不能检查一个对象/原始类型/任何类型是否与“我手中拿着的”(即作为参数传递的)相同的引用? - Daniel
这是Swift的一种语言特性。值类型更安全,因为更改一个实例不会更改“引用”的每个其他实例。无论值类型还是引用类型,在它们自己的方式上都很有价值。 - Aaron Rasmussen
有道理。同意。 - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接