函数式编程与文本解析,review octree/pretty

1,203 阅读4分钟

功能介绍:

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)
        }
    }

小结: many 先处理一次,再循环处理,纯属多余

函数式编程,第一眼神奇。多玩玩,归于平凡

github repo one

github repo two: octree/pretty