TXR Lisp中的解决方案:
$ txr common-word-lines.tl file1 file2
酒吧 1 酒吧 3
酒吧 2 酒吧 3
common-word-lines.tl中的代码:
(defun hash-file-words (name)
(with-stream (s (record-adapter #/\s+/ (open-file name "r")))
(hash-list (get-lines s) :equal-based)))
(defun lines-containing-words-in-both-hashes (name hash1 hash2)
(let ((s (open-file name "r")))
(mappend*
(op if [some (tok-str @1 #/\S+/) (andf hash1 hash2)]
(list @1))
(get-lines s))))
(tree-case *args*
((file1 file2 extra . junk) (throwf 'error "too many arguments"));
((file1 file2)
(let ((hash1 (hash-file-words file1))
(hash2 (hash-file-words file2)))
(put-lines (lines-containing-words-in-both-hashes file1 hash1 hash2))
(put-lines (lines-containing-words-in-both-hashes file2 hash1 hash2))))
(else (throwf 'error "insufficient arguments")))
这会两次遍历文件。在第一遍中,我们构建了两个文件中所有以空格分隔的单词的哈希。在第二遍中,我们打印每个文件中的每一行,其中至少包含一个出现在两个哈希中的单词。
使用了惰性列表处理,因此虽然看起来我们正在一次读取整个文件,但实际上并非如此。 get-lines 返回一个惰性列表。在hash-file-words 中,文件实际上正在被读取,因为hash-list 函数正在沿着传递给它的惰性列表前进。在lines-containing-words-in-both-hashes 中,使用了mappend*,它懒惰地过滤列表并附加片段。
(andf hash1 hash2) 是什么?首先,andf 是一个组合子。它接受多个都是函数的参数,并返回一个函数,该函数是这些函数的短路与组合。 (andf a b c) 产生一个函数,该函数将其参数传递给函数a。如果返回nil (false),它将停止并返回nil。否则,它将其参数传递给b,并应用相同的逻辑。如果它一直到达c,则返回c 返回的任何值。其次,虽然hash1 和hash2 是哈希表,但是它们可以在TXR Lisp 中用作函数。哈希表表现为一个单参数函数,它在哈希表中查找其参数,并返回相应的值,否则nil。因此,(andf hash1 hash2) 只需使用 AND 组合符来构建一个函数,如果其参数存在于两个哈希表中(与非nil 值相关联),则该函数返回 true。
因此,[some (tok-str @1 #/\S+/) (andf hash1 hash2)] 的意思是“将行标记为单词,并报告其中一些是否在两个哈希中”。 @1 是(op ...) 宏生成的匿名函数的隐式参数。为(get-lines) 生成的列表中的每个元素调用该函数;即文件的每一行。所以@1依次表示每一行。
更通用的版本:更短,并处理两个或多个参数:
(defun hash-file-words (name)
(with-stream (s (record-adapter #/\s+/ (open-file name "r")))
(hash-list (get-lines s) :equal-based)))
(defun lines-containing-words-in-all-hashes (name hashes)
(let ((s (open-file name "r")))
(mappend*
(op if [some (tok-str @1 #/\S+/) (andf . hashes)]
(list @1))
(get-lines s))))
(unless *args*
(put-line `specify one or more files`)
(exit 1))
(let ((word-hashes [mapcar hash-file-words *args*]))
(each ((file *args*))
(put-lines (lines-containing-words-in-all-hashes file word-hashes))))