功能介绍:
octree/pretty. 根据 Podfile.lock 生成依赖图
函数式编程, 构建数据管道
下面的代码,把 Podfile.lock
里面的字符串,转化为字典 [库: [依赖库]],
对新手,具有很强的劝退力量
let (dependency, _) = PodLockFileParser.parse(Substring(string))
其中 PodLockFileParser
let PodLockFileParser = pods *> dependencyItems
其中 dependencyItems
private let item = (indentation *> hyphon *> space *> quote.optional *> word)
<* (space.followed(by: version)).optional <* quote.optional <* colon.optional <* newLine
private let subItem = indentation *> item
private let dependencyItem = curry(dependencyCombine) <^> item <*> subItem.many.optional
private let dependencyItems = dependencyItem.many.map { x -> [String : [String]] in
var map = [String: [String]]()
x.forEach { map[$0.0] = $0.1 }
return map
}
看懂上面的函数式编程代码,比实现这个功能,似乎要难一些
大致套路
函数式编程, 大量使用匿名函数单元,和操作符,实现数据管道。
每一个匿名函数单元,都是数据流里面,可信赖的一个环节
操作符,可以简化方法的书写。操作符展开,就是链式函数
实际的一行数据
- Moya/Core (13.0.1):
对应这一个匿名函数
// (两个空格 *> 连字号 *> 空格 *> 可能有个引号 *> 单词)<* 可能有个(空格带版本号)<* 可能有个引号 <* 可能有个冒号 <* 换行符
let item = (indentation *> hyphon *> space *> quote.optional *> word)
<* (space.followed(by: version)).optional <* quote.optional <* colon.optional <* newLine
感觉,函数式编程,有很强的扭曲力。强制让数据流,与数据格式,高度吻合
*>
, 代表,去除左边的数据
因为我们关心的,是库的名字,例如这样 Moya/Core
, 实际这个 word
<*
, 代表,去除右边的数据
再看一句
实际的一行子模块数据
- Result (~> 4.1)
对应这一个匿名函数
// 两个空格 *> 一行
// 因为子模块,也就比模块,前面多了两个空格
let subItem = indentation *> item
三个基础文本处理单元构建方法:
// 解析指定的字符
// 解析不到,就失败,返回 nil
// 解析成功,就返回元组 ( 指定的字符, 剩下的字符串 )
// 从上面的逻辑,可看出,解析成功,返回的元组中的字符,被丢弃
// 解析成功,返回的元组中的剩下的字符串,保留
func character(matching condition: @escaping (Character) -> Bool) -> Parser<Character> {
return Parser(parse: { input in
guard let char = input.first, condition(char) else {
return nil
}
return (char, input.dropFirst())
})
}
// 解析指定的字符
// 这个也就简单调用上面那个
// 这个方法,与上面那个,可合并
func character(_ ch: Character) -> Parser<Character> {
return character {
$0 == ch
}
}
// 解析指定的字符串
// 解析不到,就失败,返回 nil
// 解析成功,就返回元组 ( 指定的字符串, 剩下的字符串 )
// 从上面的逻辑,可看出,解析成功,返回的元组中的信息,都保留
func string(_ string: String) -> Parser<String> {
return Parser { input in
guard input.hasPrefix(string) else {
return nil
}
return (string, input.dropFirst(string.count))
}
}
上面看到的, 一行文字处理
private let item = (indentation *> hyphon *> space *> quote.optional *> word)
<* (space.followed(by: version)).optional <* quote.optional <* colon.optional <* newLine
头一个匿名函数,处理缩进
/// 空格
private let space = character(" ")
// 缩进, 处理一个空格,再处理一个空格
private let indentation = space.followed(by: space)
实现:
*>
, 代表去除左边的数据
这个操作符,是怎么实现的
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
// 这里将有改动
return curry({ _, y in y }) <^> lhs <*> rhs
}
func <^><A, B>(lhs: @escaping (A) -> B, rhs: Parser<A>) -> Parser<B> {
return rhs.map(lhs)
}
展开一次, 开 <^>
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
// 这里有改动
return lhs.map(curry({ _, y in y })) <*> rhs
}
func <*><A, B>(lhs: Parser<(A) -> B>, rhs: Parser<A>) -> Parser<B> {
return lhs.followed(by: rhs).map { $0($1) }
}
展开 2, 开 <*>
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
// 这里有改动
return lhs.map(curry({ _, y in y })).followed(by: rhs).map { $0($1) }
}
public func curry<T, U, V>(_ f: @escaping (T, U) -> V) -> (T) -> (U) -> V {
return { x in { y in f(x, y) } }
}
展开 3, 开 curry
想要把 curry 这个特性,用好,难
等价于 curry 化,一般没啥用
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
// 这里有改动
return lhs.map({ x in { y in y } }).followed(by: rhs).map { $0($1) }
}
// 这里的 Result, 对应 self 的
// 具体的 parse 结构体,看 github repo
func map<T>(_ transform: @escaping (Result) -> T) -> Parser<T> {
return Parser<T> {
input in
guard let (result, remainder) = self.parse(input) else {
return nil
}
return (transform(result), remainder)
}
}
展开 4, 开第一个 map
这里较容易,得到传入第一个 map
的变形函数是 { y in y }
这个函数的签名,表面看可以是 (B) -> B
, 也可以是 (A) -> A
具体是从后面的上下文看出。
*>
的实现,可清晰看出了
左边参数解析后的 result
, 没有使用,等价于直接丢弃
侧面反映了函数式编程的缺点,使用管道,必须改造数据,使数据合规。
管道化,需要的语法糖,也挺多的
这里的 curry 化, 就是为了让数据合规
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
// 这里有改动
return Parser<(B) -> B> {
input in
guard let (result, remainder) = lhs.parse(input) else {
return nil
}
return ({ y in y }, remainder)
}.followed(by: rhs).map { $0($1) }
}
// 就是处理两次的意思,
func followed<A>(by other: Parser<A>) -> Parser<(Result, A)> {
return Parser<(Result, A)> {
input in
// self 解析一次,other 解析一次
// 出错,就终结, 标准的套路
guard let (first, reminder) = self.parse(input),
let (second, newReminder) = other.parse(reminder) else {
return nil
}
return ((first, second), newReminder)
}
}
展开 5, 开 followed
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
let first = Parser<(B) -> B> {
input in
// 左边处理的结果,直接丢弃
guard let (_, remainder) = lhs.parse(input) else {
return nil
}
return ({ y in y }, remainder)
}
// 这里有改动
let second = Parser<((B) -> B, B)> {
input in
guard let (_, reminder) = first.parse(input),
let (second, newReminder) = rhs.parse(reminder) else {
return nil
}
return (({ y in y }, second), newReminder)
}
return second.map { $1 }
}
等价于
// followed, 只是简单的连接器
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
let second = Parser<((B) -> B, B)> {
input in
// 这里有改动
guard let (_, reminder) = lhs.parse(input),
let (second, newReminder) = rhs.parse(reminder) else {
return nil
}
return (({ y in y }, second), newReminder)
}
return second.map { $1 }
}
等价于
curry 化,这里就是为了转格式
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
let second = Parser<B> {
input in
guard let (_, reminder) = lhs.parse(input),
let (second, newReminder) = rhs.parse(reminder) else {
return nil
}
// 这里有改动
return (second, newReminder)
}
return second.map { $0 }
}
// 再看 map, map 就是转下格式 `transform(result)`
func map<T>(_ transform: @escaping (Result) -> T) -> Parser<T> {
return Parser<T> {
input in
// map 做的事情,很少。下面这句是,标准套路
guard let (result, remainder) = self.parse(input) else {
return nil
}
return (transform(result), remainder)
}
}
展开 6, 去末尾的 map
因为上面的 map 啥事没做,可以直接去除
下面的代码,逻辑就很清晰了
忽略左边等价于,
处理左边,结果不要,
再处理右边,结果保留
func *><A, B>(lhs: Parser<A>, rhs: Parser<B>) -> Parser<B> {
// 这里有改动
return Parser<B>{
input in
guard let (_, reminder) = lhs.parse(input),
let (second, newReminder) = rhs.parse(reminder) else {
return nil
}
return (second, newReminder)
}
}
小结:函数式编程,没想象的那么神奇,套路其实挺明确的
代码优化
优化操作符 many
extension Parser {
private var _many: Parser<[Result]> {
return Parser<[Result]> {
input in
var result: [Result] = []
var remainder = input
while let (element, newRemainder) = self.parse(remainder) {
result.append(element)
remainder = newRemainder
}
return (result, remainder)
}
}
var many: Parser<[Result]> {
// 这里将有改动
return curry { [$0] + $1 } <^> self <*> self._many
}
}
展开 a, 去 <^>
var many: Parser<[Result]> {
return map(curry { [$0] + $1 }) <*> self._many
}
展开 b, 去 <*>
var many: Parser<[Result]> {
return map(curry { [$0] + $1 }).followed(by: self._many).map { $0($1) }
}
展开 c, 去第一个 map
var many: Parser<[Result]> {
// 这里有改动
let one = Parser<([Result]) -> [Result]> {
input in
guard let (result, remainder) = self.parse(input) else {
return nil
}
return (curry { [$0] + $1 }(result), remainder)
}
return one.followed(by: self._many).map { $0($1) }
}
展开 d, 去第一个 followed
var many: Parser<[Result]> {
// 这里是,转化
let one = Parser<([Result]) -> [Result]> {
input in
guard let (result, remainder) = self.parse(input) else {
return nil
}
return (curry { [$0] + $1 }(result), remainder)
}
// 这里是,连接
// 这里有改动
let two = Parser<(([Result]) -> [Result], [Result])>{
input in
guard let (first, reminder) = one.parse(input),
let (second, newReminder) = self._many.parse(reminder) else {
return nil
}
return ((first, second), newReminder)
}
return two.map { $0($1) }
}
可合并
var many: Parser<[Result]> {
let two = Parser<(([Result]) -> [Result], [Result])>{
input in
// 这里有改动
guard let (first, reminder) = parse(input),
let (second, newReminder) = self._many.parse(reminder) else {
return nil
}
return (((curry { [$0] + $1 })(first), second), newReminder)
}
return two.map { $0($1) }
}
展开 e, 去结尾的 map
var many: Parser<[Result]> {
let two = Parser<(([Result]) -> [Result], [Result])>{
input in
guard let (first, reminder) = parse(input),
let (second, newReminder) = self._many.parse(reminder) else {
return nil
}
return (((curry { [$0] + $1 })(first), second), newReminder)
}
// 这里有改动
return Parser<[Result]> {
input in
guard let (result, remainder) = two.parse(input) else {
return nil
}
return (result.0(result.1), remainder)
}
}
展开 f, 去 curry
var many: Parser<[Result]> {
let two = Parser<(([Result]) -> [Result], [Result])>{
input in
guard let (first, reminder) = parse(input),
let (second, newReminder) = self._many.parse(reminder) else {
return nil
}
// 这里有改动
return (({ x in { y in [x] + y } }(first), second), newReminder)
}
return Parser<[Result]> {
input in
guard let (result, remainder) = two.parse(input) else {
return nil
}
return (result.0(result.1), remainder)
}
}
等价于
var many: Parser<[Result]> {
// 这就是两次处理
let two = Parser<(([Result]) -> [Result], [Result])>{
input in
guard let (first, reminder) = parse(input),
let (second, newReminder) = self._many.parse(reminder) else {
return nil
}
// 这里有改动
return (( { y in [first] + y }, second), newReminder)
}
// 这就是一层变换
return Parser<[Result]> {
input in
guard let (result, remainder) = two.parse(input) else {
return nil
}
return (result.0(result.1), remainder)
}
}
展开 g, 合并
完成后,发现,
many
操作符,就是自个处理了一遍,交个 _many
去循环处理
private var _many: Parser<[Result]> {
return Parser<[Result]> {
input in
var result: [Result] = []
var remainder = input
while let (element, newRemainder) = self.parse(remainder) {
result.append(element)
remainder = newRemainder
}
return (result, remainder)
}
}
var many: Parser<[Result]> {
// 这里有改动
return Parser<[Result]>{
input in
guard let (first, reminder) = parse(input),
let (second, newReminder) = self._many.parse(reminder) else {
return nil
}
return ( [first] + second, newReminder)
}
}
_many
等价于 many
,
var many: Parser<[Result]> {
return Parser<[Result]> {
input in
var result: [Result] = []
var remainder = input
while let (element, newRemainder) = self.parse(remainder) {
result.append(element)
remainder = newRemainder
}
return (result, remainder)
}
}