1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

[Haml] Add an LCS algorithm to Haml::Util.

This commit is contained in:
Nathan Weizenbaum 2010-05-07 17:30:43 -07:00
parent 26bfe0c867
commit 0ffa99ebde
2 changed files with 85 additions and 0 deletions

View file

@ -193,6 +193,30 @@ module Haml
end
end
# Computes a single longest common subsequence for `x` and `y`.
# If there are more than one longest common subsequences,
# the one returned is that which starts first in `x`.
#
# @param x [Array]
# @param y [Array]
# @return [Array] The LCS
def lcs(x, y)
x = [nil, *x]
y = [nil, *y]
lcs_backtrace(lcs_table(x, y), x, y, x.size-1, y.size-1)
end
# Computes all single longest common subsequences for `x` and `y`.
#
# @param x [Array]
# @param y [Array]
# @return [Set<Array>] The LCSes
def lcs_all(x, y)
x = [nil, *x]
y = [nil, *y]
lcs_backtrace_all(lcs_table(x, y), x, y, x.size-1, y.size-1)
end
# Returns information about the caller of the previous method.
#
# @param entry [String] An entry in the `#caller` list, or a similarly formatted string
@ -564,5 +588,46 @@ METHOD
def static_method_name(name, *vars)
"#{name}_#{vars.map {|v| !!v}.join('_')}"
end
private
# Calculates the memoization table for the Least Common Subsequence algorithm.
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
def lcs_table(x, y)
c = Array.new(x.size) {[]}
x.size.times {|i| c[i][0] = 0}
y.size.times {|j| c[0][j] = 0}
(1...x.size).each do |i|
(1...y.size).each do |j|
c[i][j] =
if x[i] == y[j]
c[i-1][j-1] + 1
else
[c[i][j-1], c[i-1][j]].max
end
end
end
return c
end
# Computes a single longest common subsequence for arrays x and y.
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
def lcs_backtrace(c, x, y, i, j)
return [] if i == 0 || j == 0
return lcs_backtrace(c, x, y, i-1, j-1) << x[i] if x[i] == y[j]
return lcs_backtrace(c, x, y, i, j-1) if c[i][j-1] > c[i-1][j]
return lcs_backtrace(c, x, y, i-1, j)
end
# Computes all longest common subsequences for arrays x and y.
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_all_LCSs)
def lcs_backtrace_all(c, x, y, i, j)
return Set[[]] if i == 0 || j == 0
return lcs_backtrace_all(c, x, y, i-1, j-1).map {|z| z << x[i]}.to_set if x[i] == y[j]
r = Set.new
r.merge(lcs_backtrace_all(c, x, y, i, j-1)) if c[i][j-1] >= c[i-1][j]
r.merge(lcs_backtrace_all(c, x, y, i-1, j)) if c[i-1][j] >= c[i][j-1]
r
end
end
end

View file

@ -100,6 +100,26 @@ class UtilTest < Test::Unit::TestCase
assert_equal([[1, 2, 3]], paths([[1], [2], [3]]))
end
def test_lcs
assert_equal([1, 2, 3], lcs([1, 2, 3], [1, 2, 3]))
assert_equal([], lcs([], [1, 2, 3]))
assert_equal([], lcs([1, 2, 3], []))
assert_equal([1, 2, 3], lcs([5, 1, 4, 2, 3, 17], [0, 0, 1, 2, 6, 3]))
assert_equal([1], lcs([1, 2, 3, 4], [4, 3, 2, 1]))
assert_equal([1, 2], lcs([1, 2, 3, 4], [3, 4, 1, 2]))
end
def test_lcs_all
assert_equal(Set[[1, 2, 3]], lcs_all([1, 2, 3], [1, 2, 3]))
assert_equal(Set[[]], lcs_all([], [1, 2, 3]))
assert_equal(Set[[]], lcs_all([1, 2, 3], []))
assert_equal(Set[[1, 2, 3]], lcs_all([5, 1, 4, 2, 3, 17], [0, 0, 1, 2, 6, 3]))
assert_equal(Set[[1], [2], [3], [4]], lcs_all([1, 2, 3, 4], [4, 3, 2, 1]))
assert_equal(Set[[1, 2], [3, 4]], lcs_all([1, 2, 3, 4], [3, 4, 1, 2]))
end
def test_silence_warnings
old_stderr, $stderr = $stderr, StringIO.new
warn "Out"