1
1
mirror of https://github.com/github/semantic.git synced 2025-01-04 13:34:31 +03:00

Allow recurrence to opt out of comparing nodes by returning nil.

This commit is contained in:
Rob Rix 2015-10-13 17:40:14 -04:00
parent 873a8e668f
commit 90b22cf9a8
2 changed files with 7 additions and 7 deletions

View File

@ -78,7 +78,7 @@ public enum Algorithm<A, B> {
/// Evaluates the encoded algorithm, returning its result.
public func evaluate(equals: (A, A) -> Bool, recur: (Term, Term) -> Diff) -> B {
public func evaluate(equals: (A, A) -> Bool, recur: (Term, Term) -> Diff?) -> B {
func replace(a: Term, _ b: Term) -> Diff {
return .Pure(.Replace(a, b))
}
@ -94,10 +94,10 @@ public enum Algorithm<A, B> {
switch (a.out, b.out) {
case let (.Indexed(a), .Indexed(b)) where a.count == b.count:
return f(.Indexed(zip(a, b).map(recur))).evaluate(equals, recur: recur)
return f(.Indexed(zip(a, b).map { recur($0, $1) ?? replace($0, $1) })).evaluate(equals, recur: recur)
case let (.Keyed(a), .Keyed(b)) where Array(a.keys) == Array(b.keys):
return f(.Keyed(Dictionary(elements: b.keys.map { ($0, recur(a[$0]!, b[$0]!)) }))).evaluate(equals, recur: recur)
return f(.Keyed(Dictionary(elements: b.keys.map { ($0, recur(a[$0]!, b[$0]!) ?? replace(a[$0]!, b[$0]!)) }))).evaluate(equals, recur: recur)
default:
// This must not call `recur` with `a` and `b`, as that would infinite loop if actually recursive.
@ -113,7 +113,7 @@ public enum Algorithm<A, B> {
// Essentially [set reconciliation](https://en.wikipedia.org/wiki/Data_synchronization#Unordered_data) on the keys, followed by recurring into the values of the intersecting keys.
let deleted = Set(a.keys).subtract(b.keys).map { ($0, Diff.Pure(Patch.Delete(a[$0]!))) }
let inserted = Set(b.keys).subtract(a.keys).map { ($0, Diff.Pure(Patch.Insert(b[$0]!))) }
let patched = Set(a.keys).intersect(b.keys).map { ($0, recur(a[$0]!, b[$0]!)) }
let patched = Set(a.keys).intersect(b.keys).map { ($0, recur(a[$0]!, b[$0]!) ?? replace(a[$0]!, b[$0]!)) }
return f(Dictionary(elements: deleted + inserted + patched)).evaluate(equals, recur: recur)
case let .Roll(.ByIndex(a, b, f)):
@ -123,7 +123,7 @@ public enum Algorithm<A, B> {
}
extension Algorithm where A: Equatable {
public func evaluate(recur: (Term, Term) -> Diff) -> B {
public func evaluate(recur: (Term, Term) -> Diff?) -> B {
return evaluate(==, recur: recur)
}
}

View File

@ -1,7 +1,7 @@
/// Computes the SES (shortest edit script), i.e. the shortest sequence of diffs (`Free<A, Patch<A>>`) for two arrays of terms (`Fix<A>`) which would suffice to transform `a` into `b`.
///
/// This is computed w.r.t. an `equals` function, which computes the equality of leaf nodes within terms, and a `recur` function, which produces diffs representing matched-up terms.
public func SES<A>(a: [Fix<A>], _ b: [Fix<A>], equals: (A, A) -> Bool, recur: (Fix<A>, Fix<A>) -> Free<A, Patch<A>>) -> [Free<A, Patch<A>>] {
public func SES<A>(a: [Fix<A>], _ b: [Fix<A>], equals: (A, A) -> Bool, recur: (Fix<A>, Fix<A>) -> Free<A, Patch<A>>?) -> [Free<A, Patch<A>>] {
typealias Term = Fix<A>
typealias Diff = Free<A, Patch<A>>
@ -55,7 +55,7 @@ public func SES<A>(a: [Fix<A>], _ b: [Fix<A>], equals: (A, A) -> Bool, recur: (F
if let right = right, down = down, diagonal = diagonal {
// nominate the best edge to continue along
let (best, diff, _) = min(
(diagonal, recur(a[i], b[j]), costOfStream(diagonal)),
(diagonal, recur(a[i], b[j]) ?? .Pure(.Replace(a[i], b[j])), costOfStream(diagonal)),
(right, Diff.Pure(Patch.Delete(a[i])), costOfStream(right)),
(down, Diff.Pure(Patch.Insert(b[j])), costOfStream(down))) { $0.2 < $1.2 }
return cons(diff, rest: best)