Swift:延迟封装地图,过滤器,flatMap链 [英] Swift: Lazily encapsulating chains of map, filter, flatMap

查看:103
本文介绍了Swift:延迟封装地图,过滤器,flatMap链的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有动物清单:

let animals = ["bear", "dog", "cat"]

以及一些转换列表的方法:

And some ways to transform that list:

typealias Transform = (String) -> [String]

let containsA: Transform = { $0.contains("a") ? [$0] : [] }
let plural:    Transform = { [$0 + "s"] }
let double:    Transform = { [$0, $0] }

略有不同,它们分别类似于filter(输出0或1个元素),map(恰好1个元素)和flatmap(超过1个元素),但是以统一的方式定义,以便可以一致地处理它们.

As a slight aside, these are analogous to filter (outputs 0 or 1 element), map (exactly 1 element) and flatmap (more than 1 element) respectively but defined in a uniform way so that they can be handled consistently.

我想创建一个惰性迭代器,将这些转换的数组应用于动物列表:

I want to create a lazy iterator which applies an array of these transforms to the list of animals:

extension Array where Element == String {
  func transform(_ transforms: [Transform]) -> AnySequence<String> {

    return AnySequence<String> { () -> AnyIterator<String> in
      var iterator = self
        .lazy
        .flatMap(transforms[0])
        .flatMap(transforms[1])
        .flatMap(transforms[2])
        .makeIterator()

      return AnyIterator {
        return iterator.next()
      }
    }
  }
}

这意味着我可以偷懒做:

which means I can lazily do:

let transformed = animals.transform([containsA, plural, double])

并检查结果:

print(Array(transformed))

我很高兴这很简洁,但很明显:

I'm pleased with how succinct this is but clearly:

        .flatMap(transforms[0])
        .flatMap(transforms[1])
        .flatMap(transforms[2])

是一个问题,因为这意味着转换功能仅适用于3个转换的数组.

is an issue as it means the transform function will only work with an array of 3 transforms.

修改: 我试过了:

  var lazyCollection = self.lazy
  for transform in transforms {
    lazyCollection = lazyCollection.flatMap(transform) //Error
  }
  var iterator = lazyCollection.makeIterator()

但是在标记的行上我得到了错误:

but on the marked row I get error:

无法分配类型为"LazyCollection"的值FlattenCollection< LazyMapCollection<阵列< String>,[String] >>>'键入"LazyCollection<阵列<字符串>>'

我了解这是因为每次循环时都会添加另一个平面图,因此类型在变化.

which I understand because each time around the loop another flatmap is being added, so the type is changing.

如何使转换函数可以处理任意数量的转换数组?

How can I make the transform function work with an array of any number of transforms?

一种用于有限数量转换的WET解决方案是(但YUK!)

  switch transforms.count {
  case 1:
    var iterator = self
      .lazy
      .flatMap(transforms[0])
      .makeIterator()
    return AnyIterator {
      return iterator.next()
    }
  case 2:
    var iterator = self
      .lazy
      .flatMap(transforms[0])
      .flatMap(transforms[1])
      .makeIterator()
    return AnyIterator {
      return iterator.next()
    }
  case 3:
    var iterator = self
      .lazy
      .flatMap(transforms[0])
      .flatMap(transforms[1])
      .flatMap(transforms[2])
      .makeIterator()
    return AnyIterator {
      return iterator.next()
    }
  default:
    fatalError(" Too many transforms!")
  }

完整代码:

let animals = ["bear", "dog", "cat"]

typealias Transform = (String) -> [String]

let containsA: Transform = { $0.contains("a") ? [$0] : [] }
let plural:    Transform = { [$0 + "s"] }
let double:    Transform = { [$0, $0] }

extension Array where Element == String {
  func transform(_ transforms: [Transform]) -> AnySequence<String> {

    return AnySequence<String> { () -> AnyIterator<String> in
      var iterator = self
        .lazy
        .flatMap(transforms[0])
        .flatMap(transforms[1])
        .flatMap(transforms[2])
        .makeIterator()

      return AnyIterator {
        return iterator.next()
      }
    }
  }
}

let transformed = animals.transform([containsA, plural, double])

print(Array(transformed))

推荐答案

另一种实现所需目标的方法:

Another approach to achieve what you want:

修改:我尝试过:

var lazyCollection = self.lazy
for transform in transforms {
    lazyCollection = lazyCollection.flatMap(transform) //Error
}
var iterator = lazyCollection.makeIterator()

您已经很接近目标了,如果 Error 行中的两种类型都可以分配,那么您的代码就可以了.

You were very near to your goal, if the both types in the Error line was assignable, your code would have worked.

一些修改:

var lazySequence = AnySequence(self.lazy)
for transform in transforms {
    lazySequence = AnySequence(lazySequence.flatMap(transform))
}
var iterator = lazySequence.makeIterator()

或者您可以在此处使用reduce:

Or you can use reduce here:

var transformedSequence = transforms.reduce(AnySequence(self.lazy)) {sequence, transform in
    AnySequence(sequence.flatMap(transform))
}
var iterator = transformedSequence.makeIterator()

整个代码为:

(编辑,已修改为包括Martin R.的建议.)

(EDIT Modified to include the suggestions from Martin R.)

let animals = ["bear", "dog", "cat"]

typealias Transform<Element> = (Element) -> [Element]

let containsA: Transform<String> = { $0.contains("a") ? [$0] : [] }
let plural:    Transform<String> = { [$0 + "s"] }
let double:    Transform<String> = { [$0, $0] }

extension Sequence {
    func transform(_ transforms: [Transform<Element>]) -> AnySequence<Element> {
        return transforms.reduce(AnySequence(self)) {sequence, transform in
            AnySequence(sequence.lazy.flatMap(transform))
        }
    }
}

let transformed = animals.transform([containsA, plural, double])

print(Array(transformed))

这篇关于Swift:延迟封装地图,过滤器,flatMap链的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆