当前位置: 首页 > 面试题库 >

如何创建懒惰组合

翟鹏
2023-03-14
问题内容

我的问题很简单,如何使这段代码变得懒惰:

/*
input: [
    [1, 2],
    [3, 4],
    [5, 6]
]

output: [
    [1, 3, 5],
    [1, 3, 6],
    [1, 4, 5],
    [1, 4, 6],
    [2, 3, 5],
    [2, 3, 6],
    [2, 4, 5],
    [2, 4, 6],
]
*/

func combinations<T>(options: [[T]]) -> [[T]] {
    guard let head = options.first else {
        return [].map({ [$0] })
    }

    if options.count == 1 {
        return head.map({ [$0] })
    }

    let tailCombinations = combinations(options: Array(options.dropFirst()))

    return head.flatMap({ option in
        return tailCombinations.map({ combination -> [T] in
            return [option] + combination
        })
    })
}

上面的代码可以计算组合,但是可以在内存中创建整个数组数组。我需要让它返回类似的东西LazySequence<Array<T>>,除了Swift类型系统不允许我做一些通用的事情。

有什么想法如何实现这一目标并保持功能风格吗?

附注:我确实想过用生成器解决这个问题并跟踪索引的另一种方法,但是我不想跟踪任何状态,我想要一个纯函数式(如FP中)的解决方案。Haskell默认情况下会这样做,顺便说一句,我正在寻找相同的东西。

编辑: 我已经设法解决了部分问题,类型系统,AnyCollection

func combinations<T>(options: [[T]]) -> LazyCollection<AnyCollection<[T]>> {
    guard let head = options.first else {
        return AnyCollection([].lazy.map({ [$0] })).lazy
    }

    if options.count == 1 {
        return AnyCollection(head.lazy.map({ [$0] })).lazy
    }

    let tailCombinations = combinations(options: Array(options.dropFirst()))

    return AnyCollection(head.lazy.flatMap({ option in
        return tailCombinations.lazy.map({ [option] + $0 })
    })).lazy
}

但是,当我使用该函数时,它将整个集合加载到内存中,即不是惰性的。

编辑2: 做更多的调查,结果问题出在AnyCollection

// stays lazy
let x1 = head.lazy.flatMap({ option in
    return tailCombinations.lazy.map({ [option] + $0 })
})

// forces to load in memory
let x2 = AnyCollection(head.lazy.flatMap({ option in
    return tailCombinations.lazy.map({ [option] + $0 })
}))

尚不确定如何解决此问题。


问题答案:

这是我想出的:

func combinations<T>(options: [[T]]) -> AnySequence<[T]> {
    guard let lastOption = options.last else {
        return AnySequence(CollectionOfOne([]))
    }
    let headCombinations = combinations(options: Array(options.dropLast()))
    return AnySequence(headCombinations.lazy.flatMap { head in
        lastOption.lazy.map { head + [$0] }
    })
}

此解决方案的主要区别在于,递归调用创建了 第一个
N-1选项的序列,然后将该序列的每个元素与最后一个选项的每个元素组合在一起。这是更有效的,因为从递归调用返回的序列仅被枚举一次,而不是与其组合的每个元素一次。

其他区别是:

  • 如果该序列已经是惰性的.lazyAnySequence则无需调用。因此,返回类型“简化为” AnySequence<[T]>
  • 我曾经CollectionOfOne为空数组创建一个单元素序列。
  • options.count == 1对于算法来说,分开处理案例不是必需的(但可能会提高性能)。

完全不同的方法是定义一个自定义 集合类型 ,该 类型 使用简单的模运算来计算每个组合作为索引的函数:

struct Combinations<T> : RandomAccessCollection {
    let options: [[T]]
    let startIndex = 0
    let endIndex: Int

    init(options: [[T]]) {
        self.options = options.reversed()
        self.endIndex = options.reduce(1) { $0 * $1.count }
    }

    subscript(index: Int) -> [T] {
        var i = index
        var combination: [T] = []
        combination.reserveCapacity(options.count)
        options.forEach { option in
            combination.append(option[i % option.count])
            i /= option.count
        }
        return combination.reversed()
    }
}

无需额外的存储,也无需递归。用法示例:

let all = Combinations(options: [[1, 2], [3, 4], [5, 6]])
print(all.count)
for c in all { print(c) }

输出:

8
[1、3、5]
[1、3、6]
[1,4,5]
[1,4,6]
[2,3,5]
[2、3、6]
[2,4,5]
[2,4,6]

用测试

let options = Array(repeating: [1, 2, 3, 4, 5], count: 5)

这种基于集合的方法比上面基于序列的方法要快2倍。



 类似资料:
  • 我想创建可以从文件系统中为资源服务的参与者。理想情况下,[1]我希望每个目录和每个文件都有一个参与者。但是我不想创建整个actor树层次结构,因为我希望尽可能节省内存和资源。 据我所知,只有当它的父级存在时,才能创建一个演员。懒洋洋地创建这些层次结构的最佳方法是什么。是否有一个钩子可以用来捕捉失败并在飞行中创建参与者层次结构,并有效地这样做? 这样,我就可以向参与者发送、、、...消息,从而使ak

  • 在某些情况下,我的广播接收器是不需要的,所以需要检查接收器是否为空,但有些如何这个对象不为空,即使不使用它和造成崩溃。

  • 我正在尝试实现一个流,该流在其实现中使用自身的另一个实例。流前面有几个常量元素(使用IntStream.concat),所以只要连接的流懒散地创建非常量部分,这就可以工作。我想使用StreamSupport。intStream重载使用intStream获取供应商。concat(它“创建一个懒散连接的流”)应该足够懒惰,只在需要元素时创建第二个拆分器,但即使创建流(而不是计算流)也会溢出堆栈。我如何

  • 我有一个数据表的问题-懒加载。我认为问题是在IdiomasBean.java(TableBean.java),如果我把: 我得到了正确的数据表,但是<代码>按排序、筛选和不起作用。 我得到:java。lang.NullPointerException这里是堆栈跟踪: 下面是代码的其余部分: 指数xhtml diomasBean.java 懒散的数据模型。JAVA IdiomasBo.java 习语

  • 问题内容: 我想创建自己的集合,该集合具有python list的所有属性,并且还知道如何将自身保存到数据库中或从数据库中加载。我也想使负载隐式和惰性,因为在列表创建时它不会发生,而是等到第一次使用时才发生。 有没有一种单一的方法,我可以覆盖上加载任何列表属性(如第一次使用清单,,而不必重写他们… …等)? 问题答案: 不,没有。

  • 问题内容: 在我们正在开发的此应用程序中,我们注意到一个视图特别慢。我对视图进行了概要分析,发现即使数据库中只有两个对象要获取,hibernate也执行了一个查询,该查询花费了10秒。所有和关系都是懒惰的,所以这不是问题。在检查实际执行的SQL时,我注意到查询中有80多个联接。 在进一步检查该问题时,我注意到该问题是由实体类的深入层次结构和实体之间的关系引起的。所以,我想,我只是让它们变得懒惰,应