Select 行来自两个包含匹配字符串的文件
Select lines from two files containing matching strings
假设我有两个文件A和B。A的内容:
foo1 foo2
bar1 bar3
B 的内容:
bar2 bar3
foo3 foo4
如何 select A 的第二行和 B 的第一行?没有搜索字符串。我需要 select 包含所有可能的常见字符串的行。
请注意,我不是在寻找来自两个不同文件的匹配行。所需的行不相同,但包含一个公共字符串。
任何帮助将不胜感激。谢谢!
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")))
这会两次遍历文件。在第一遍中,我们构建了两个文件中所有 space 分隔词的哈希值。在第二遍中,我们打印每个文件的每一行,其中至少包含一个出现在两个哈希值中的单词。
使用了惰性列表处理,因此虽然看起来我们正在一次读取整个文件,但实际上并非如此。 get-lines
returns a lazy list. In hash-file-words
, the file is actually being read as the hash-list
function is marching down the lazy list that is passed into it. In lines-containing-words-in-both-hashes
, mappend*
用于延迟过滤列表并附加片段。
什么是(andf hash1 hash2)
?首先,andf
是一个组合子。它接受多个参数,这些参数都是函数,returns 一个函数是这些函数的短路 AND 组合。 (andf a b c)
生成一个函数,该函数会将其参数传递给函数 a
。如果那个 returnsnil
(false),它停止并且 returns nil
。否则它将其参数传递给 b
,并应用相同的逻辑。如果它一直到达 c
,则返回 c
返回的任何值。其次,虽然 hash1
和 hash2
是散列 table,但它们可以用作 TXR Lisp 中的函数。散列 table 表现为单参数函数,它在散列 table 中查找其参数,并且 returns 对应的值,否则 nil
。因此 (andf hash1 hash2)
简单地使用 AND 组合器来构建一个函数,如果它的参数存在于两个散列 table 中(与非 nil
值关联),则该函数 returns 为真。
因此,[some (tok-str @1 #/\S+/) (andf hash1 hash2)]
表示 "tokenize the line into words, and report if some of them are in both hashes"。 @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))))
假设我有两个文件A和B。A的内容:
foo1 foo2
bar1 bar3
B 的内容:
bar2 bar3
foo3 foo4
如何 select A 的第二行和 B 的第一行?没有搜索字符串。我需要 select 包含所有可能的常见字符串的行。
请注意,我不是在寻找来自两个不同文件的匹配行。所需的行不相同,但包含一个公共字符串。
任何帮助将不胜感激。谢谢!
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")))
这会两次遍历文件。在第一遍中,我们构建了两个文件中所有 space 分隔词的哈希值。在第二遍中,我们打印每个文件的每一行,其中至少包含一个出现在两个哈希值中的单词。
使用了惰性列表处理,因此虽然看起来我们正在一次读取整个文件,但实际上并非如此。 get-lines
returns a lazy list. In hash-file-words
, the file is actually being read as the hash-list
function is marching down the lazy list that is passed into it. In lines-containing-words-in-both-hashes
, mappend*
用于延迟过滤列表并附加片段。
什么是(andf hash1 hash2)
?首先,andf
是一个组合子。它接受多个参数,这些参数都是函数,returns 一个函数是这些函数的短路 AND 组合。 (andf a b c)
生成一个函数,该函数会将其参数传递给函数 a
。如果那个 returnsnil
(false),它停止并且 returns nil
。否则它将其参数传递给 b
,并应用相同的逻辑。如果它一直到达 c
,则返回 c
返回的任何值。其次,虽然 hash1
和 hash2
是散列 table,但它们可以用作 TXR Lisp 中的函数。散列 table 表现为单参数函数,它在散列 table 中查找其参数,并且 returns 对应的值,否则 nil
。因此 (andf hash1 hash2)
简单地使用 AND 组合器来构建一个函数,如果它的参数存在于两个散列 table 中(与非 nil
值关联),则该函数 returns 为真。
因此,[some (tok-str @1 #/\S+/) (andf hash1 hash2)]
表示 "tokenize the line into words, and report if some of them are in both hashes"。 @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))))