假设我有两个文件A和B. A的内容:
foo1 foo2
bar1 bar3
B的内容:
bar2 bar3
foo3 foo4
如何从A中选择第二行,从B中选择第一行?没有搜索字符串。我需要选择包含所有可能的公共字符串的行。
请注意,我不是在寻找两个不同文件的匹配行。所需的行不相同,但包含一个共同的字符串。
任何帮助将不胜感激。谢谢!
答案 0 :(得分:1)
TXR Lisp中的解决方案:
$ txr common-word-lines.tl file1 file2 bar1 bar3 bar2 bar3
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)]
表示将该行标记为单词,并报告其中一些是否在两个哈希中#34;。 @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))))