如何从字符串值的CaseIterable枚举构建ChoiceOf正则表达式组件



当前我使用此解决方法将枚举事例列表传递给ChoiceOf

enum Fruit: String, CaseIterable {
case apple = "Apple"
case banana = "Banana"
case strawberry = "Strawberry"
}
let regex = Regex {
ChoiceOf {
try! Regex(Fruit.allCases.map(.rawValue).joined(separator: "|"))
}
}

有没有一种更优雅的方法可以做到这一点,而不使用硬编码的regex模式?类似ChoiceOf(Fruit.allCases)的东西?

这也是一种破解,但您可以看到正则表达式生成器在Swift进化方案中是如何工作的:

Regex {
regex0
regex1
regex2
regex3
}

成为

Regex {
let e0 = RegexComponentBuilder.buildExpression(regex0)
let e1 = RegexComponentBuilder.buildExpression(regex1)
let e2 = RegexComponentBuilder.buildExpression(regex2)
let e3 = RegexComponentBuilder.buildExpression(regex3)
let r0 = RegexComponentBuilder.buildPartialBlock(first: e0)
let r1 = RegexComponentBuilder.buildPartialBlock(accumulated: r0, next: e1)
let r2 = RegexComponentBuilder.buildPartialBlock(accumulated: r1, next: e2)
let r3 = RegexComponentBuilder.buildPartialBlock(accumulated: r2, next: e3)
return r3
}

我们可以在这里使用AlternationBuilder来制作ChoiceOf,而不是RegexComponentBuilder。您可以看到,buildExpressionbuildPartialBlock的调用方式类似于mapreduce

let regex = Regex {
let exps = Fruit.allCases.map { AlternationBuilder.buildExpression($0.rawValue) }
// assuming exps is not empty
exps.dropFirst().reduce(AlternationBuilder.buildPartialBlock(first: exps[0])) { acc, next in
AlternationBuilder.buildPartialBlock(accumulated: acc, next: next)
}
}

我们可以将其扩展为

extension ChoiceOf where RegexOutput == Substring {
init<S: Sequence<String>>(_ components: S) {
let exps = components.map { AlternationBuilder.buildExpression($0) }

guard !exps.isEmpty else {
fatalError("Empty choice!")
}

self = exps.dropFirst().reduce(AlternationBuilder.buildPartialBlock(first: exps[0])) { acc, next in
AlternationBuilder.buildPartialBlock(accumulated: acc, next: next)
}
}
}

值得注意的是,当数组为空时,即当没有选择时,这是不起作用的。您不能只返回:Choice { }。这违反了初始化程序的一个约束。事实上,Choice { }无论如何都没有意义。

我认为这也是为什么不支持开箱即用的原因——编译器无法确定Fruits.allCases或您给它的任何其他数组是否为空。

最新更新