TXR:如何转换连接表?

时间:2017-02-27 21:15:40

标签: text-processing

我的数据看起来像这样:

i,a,b,c
1,0.2,3.2,4.5
2,0.8,4.1,3.5
3,0.5,3.1,4.1
i,a,b,c,d
4,3.2,5.2,7.5,1.1
5,2.8,5.1,8.5,0.9
6,2.5,5.1,8.1,1.0
i,a,d
7,3.2,5.2
8,2.8,5.1
9,2.5,5.1

我想用TXR处理它,看起来像这样:

i,key,val
1,a,0.2
1,b,3.2
1,c,4.5
2,a,0.8
2,b,4.1
2,c,3.5
3,a,0.5
3,b,3.1
3,c,4.1
4,a,3.2
4,b,5.2
4,c,7.1
4,d,1.1
5,a,2.8
5,b,5.1
5,c,8.5
5,d,0.9
6,a,2.5
6,b,5.1
6,c,8.1
6,d,1.0
7,a,3.2
7,b,5.2
8,a,2.8
8,b,5.1
9,a,2.5
9,b,5.1

我目前不正确的TXR脚本是:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{key /[^,]+/}@(end)
@  (collect :gap 0)
@{i /[0-9]+/},@(coll)@{value /[^,]+/}@(end)
@  (end)
@  (output)
@    (repeat)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

它代之以:

i,key,val
1,a,0.2
1,a,3.2
1,a,4.5
2,b,0.8
2,b,4.1
2,b,3.5
3,c,0.5
3,c,3.1
3,c,4.1
4,a,3.2
4,a,5.2
4,a,7.5
4,a,1.1
5,b,2.8
5,b,5.1
5,b,8.5
5,b,0.9
6,c,2.5
6,c,5.1
6,c,8.1
6,c,1.0
7,a,3.2
7,a,5.2
8,d,2.8
8,d,5.1
9,,2.5
9,,5.1

如何实现所需的输出?我可以以某种方式使用@(merge),还是需要下载到lisp?我看到有一个transpose函数可能对此有用。

1 个答案:

答案 0 :(得分:1)

代码几乎就在那里。问题是值必须与键相关联。为此,我们可以利用2016年6月29日发布的TXR 144中添加的功能::countercollect支持的coll关键字:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{keylist /[^,]+/}@(end)
@  (collect :gap 0)
@{i /[0-9]+/},@(coll :counter c)@{value /[^,]+/}@(bind key @[keylist c])@(end)
@  (end)
@  (output)
@    (repeat)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

密钥被收集到keylist变量中而不是key中,而keyvalue绑定在一起,通过keylist索引c }}。计数器c默认为0;可以使用:counter (c expr)指定不同的起始值。

但是,不要以这种笨重的方式做到这一点。我的意思是,看看我们在做什么:我们反复通过keys踩到这个计数器,只是收集与值并行的键列表。我们可以使用内部bind之外的单个coll来实现完全相同的内容,如下所示:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{keylist /[^,]+/}@(end)
@  (collect :gap 0)
@{i /[0-9]+/},@(coll)@{value /[^,]+/}@(end)
@  (bind key keylist)
@  (end)
@  (output)
@    (repeat)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

请参阅?要获取每行的value列表,我们会收集值。密钥不会因行而异,因此要获取每个key列表以与value配对,我们只需将其与keylist绑定。

如果我们想要用这个逻辑来获取打印输出,那么我们就不能在每一行之后收集行和输出。换句话说,它只是收集关键名称的练习,然后将它们与给定部分中每行的值一起转储:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{key /[^,]+/}@(end)
@  (repeat :gap 0)
@{i /[0-9]+/},@(coll)@{value /[^,]+/}@(end)
@    (output)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

可以说,对于Awk来说这是一项很好的任务。不一定是Unix,请注意:

(awk (:set fs "," ofs ",")
     (:let keys)
     (:begin (prn 'i 'key 'val))
     ((equal [f 0] "i") (set keys (rest f))
                        (next))
     (f (each ((k keys)
               (v (rest f)))
          (prn [f 0] k v))))