1 USING: accessors arrays combinators combinators.short-circuit
\r
2 kernel locals make math math.order sequences sequences.private
\r
8 : levenshtein-step ( insert delete change same? -- next )
\r
9 [ [ 1 + ] bi@ ] 2dip [ 1 + ] unless min min ;
\r
11 : lcs-step ( insert delete change same? -- next )
\r
12 1 -1/0. ? + max max ; ! -1/0. is -inf (float)
\r
14 TYPED:: loop-step ( i j matrix: array old new step -- )
\r
15 i j 1 + matrix nth-unsafe nth-unsafe ! insertion
\r
16 i 1 + j matrix nth-unsafe nth-unsafe ! deletion
\r
17 i j matrix nth-unsafe nth-unsafe ! replace/retain
\r
18 i old nth-unsafe j new nth-unsafe = ! same?
\r
20 i 1 + j 1 + matrix nth-unsafe set-nth-unsafe ; inline
\r
22 : lcs-initialize ( |str1| |str2| -- matrix )
\r
23 iota [ drop 0 <array> ] with map ;
\r
25 : levenshtein-initialize ( |str1| |str2| -- matrix )
\r
26 [ iota ] bi@ [ [ + ] curry map ] with map ;
\r
28 :: run-lcs ( old new init step -- matrix )
\r
29 old length 1 + new length 1 + init call :> matrix
\r
30 old length iota [| i |
\r
31 new length iota [| j |
\r
32 i j matrix old new step loop-step
\r
34 ] each matrix ; inline
\r
38 : levenshtein ( old new -- n )
\r
39 [ levenshtein-initialize ] [ levenshtein-step ]
\r
42 TUPLE: retain item ;
\r
43 TUPLE: delete item ;
\r
44 TUPLE: insert item ;
\r
48 TUPLE: trace-state old new table i j ;
\r
50 : old-nth ( state -- elt )
\r
51 [ i>> 1 - ] [ old>> ] bi nth-unsafe ;
\r
53 : new-nth ( state -- elt )
\r
54 [ j>> 1 - ] [ new>> ] bi nth-unsafe ;
\r
56 : top-beats-side? ( state -- ? )
\r
57 [ [ i>> ] [ j>> 1 - ] [ table>> ] tri nth-unsafe nth-unsafe ]
\r
58 [ [ i>> 1 - ] [ j>> ] [ table>> ] tri nth-unsafe nth-unsafe ] bi > ;
\r
60 : retained? ( state -- ? )
\r
62 [ i>> 0 > ] [ j>> 0 > ]
\r
63 [ [ old-nth ] [ new-nth ] bi = ]
\r
66 : do-retain ( state -- state )
\r
67 dup old-nth retain boa ,
\r
68 [ 1 - ] change-i [ 1 - ] change-j ;
\r
70 : inserted? ( state -- ? )
\r
73 [ { [ i>> zero? ] [ top-beats-side? ] } 1|| ]
\r
76 : do-insert ( state -- state )
\r
77 dup new-nth insert boa , [ 1 - ] change-j ;
\r
79 : deleted? ( state -- ? )
\r
82 [ { [ j>> zero? ] [ top-beats-side? not ] } 1|| ]
\r
85 : do-delete ( state -- state )
\r
86 dup old-nth delete boa , [ 1 - ] change-i ;
\r
88 : (trace-diff) ( state -- )
\r
90 { [ dup retained? ] [ do-retain (trace-diff) ] }
\r
91 { [ dup inserted? ] [ do-insert (trace-diff) ] }
\r
92 { [ dup deleted? ] [ do-delete (trace-diff) ] }
\r
96 : trace-diff ( old new table -- diff )
\r
97 [ ] [ first length 1 - ] [ length 1 - ] tri trace-state boa
\r
98 [ (trace-diff) ] { } make reverse! ;
\r
102 : lcs-diff ( old new -- diff )
\r
103 2dup [ lcs-initialize ] [ lcs-step ] run-lcs trace-diff ;
\r
105 : lcs ( seq1 seq2 -- lcs )
\r
106 [ lcs-diff [ retain? ] filter ] keep [ item>> ] swap map-as ;
\r