8

我正在 kdb 数据库上学习 q。我担心 q 中没有循环。我需要编写一个算法,我会在像 C 这样的冗长程序中使用几个嵌套的 for 循环编写该算法。但是在 q 中,我被无法循环的事实所困扰。

只是举一个具体的例子(其中一个),我有这个简单的向量(列表):

q)closures
price
-----
18.54
18.53
18.53
18.52
18.57
18.9 
18.9 
18.77
18.59
18.51
18.37

我需要一个向量,将这些条目组合成 3by3 并叠加,例如(使用 R 语法):closures[0:2],closures[1:3],closures[2:4],closures[3:5]...我能怎么做?

一般来说,我需要如何改变自己的心态,才能在 q 中正确编程?

非常感谢您的建议马可

4

7 回答 7

10

解决您关于“我需要如何改变心态,以正确地在 q 中编程?”的最后一点:

您需要使用 over (/)、scan (\) 和 .zs 而不是使用循环。

例如,您的问题可以通过以下方式解决:(请注意,这些实际上并不是针对您的特定问题的最佳解决方案 - 索引是那里更好的方法 - 但尽管如此,下面的这些解决方案应该有助于理解这一点)

price:18.54 18.53 18.53 18.52 18.57 18.9 18.9 18.77 18.59 18.51 18.37

q)3#'{1_x}\[8;price]
18.54 18.53 18.53
18.53 18.53 18.52
18.53 18.52 18.57
18.52 18.57 18.9
18.57 18.9  18.9
18.9  18.9  18.77
18.9  18.77 18.59
18.77 18.59 18.51
18.59 18.51 18.37

即遍历列表,每次切掉一个,每次迭代取前 3 个

或类似的

q)3#'{1 rotate x}\[8;price]
18.54 18.53 18.53
18.53 18.53 18.52
18.53 18.52 18.57
18.52 18.57 18.9
18.57 18.9  18.9
18.9  18.9  18.77
18.9  18.77 18.59
18.77 18.59 18.51
18.59 18.51 18.37

即旋转 1 八次,每次旋转取前 3

使用 .zs 方法

q){$[2<count x;enlist[3#x],.z.s 1_x;()]}[price]
18.54 18.53 18.53
18.53 18.53 18.52
18.53 18.52 18.57
18.52 18.57 18.9
18.57 18.9  18.9
18.9  18.9  18.77
18.9  18.77 18.59
18.77 18.59 18.51
18.59 18.51 18.37

即,如果还剩下至少 3 个元素,则先取 3 个元素,然后砍掉第一个元素并将相同的功能重新应用于缩短的列表。

在此示例中使用 over (/) 会很复杂,但通常 over 可用于替换“while”类型构造

i:0
a:0;
while[i<10;i+:1;a+:10] 

更好地实现使用

q){x+10}/[10;0]
100

即添加 10,十次,起始(种子)值为零。

b:();  
while[not 18~last b;b,:1?20]      

即继续附加1到20之间的随机数,直到你达到18,然后停止。

更好地实现使用

q){x,1?20}/[{not 18~last x};()]
1 2 16 5 8 18

即在1到20之间附加一个随机数,只要检查函数返回true就迭代,从()开始作为种子值

使用 scan 和 over 还有许多其他选项,具体取决于您迭代的函数是否是 monadic/diadic 等。

作为一个广泛的概括:“for i=1:10”类型的循环可以在 q 中使用“function each i”来实现,“do”类型的循环可以在 q 中使用“function/[numOfTimes;seed]”来实现,使用“function/[booleanCheckFunction;seed]”可以在 q 中实现“while”类型的循环

于 2014-08-01T13:40:13.690 回答
3
flip (-2_price;-1_1_price;2_price)

如前所述,使用内置函数总是比使用 ' 或 / 迭代更快。这就是为什么在 100 万个元素的向量上,来自最佳答案的代码需要永远完成并使用 GB 的堆。JPC 和 Harshal 的索引方法要快几英里,但上面的翻转仍然快三倍。

一旦您拥有格式的数据,您只需使用each将您的闭包应用于列表的元素。

始终关注 \t 告诉您的内容是一个好习惯 - KDB 不会为您优化任何内容。

于 2015-06-11T14:17:27.010 回答
2

KDB Q 是一种向量处理语言,它的优势在于处理列表,而不是像命令式语言那样处理单个原子。

但是,如果您不知道,Q 确实支持循环,并且可以在这些似乎没有其他方式的情况下使用!这是我开始从 Java 迁移到 KDB Q 时编写的一个示例

isPrime:{[num] i:2; flag:1b; while[i<=sqrt num; if[(num mod i)~0;flag:0b];i:i+1]; flag}

它也有do-while一种语法

    i : 0;
    do [5;
        // Iterated action involving i
        0N!i; //printing i
        i : i + 1
    ];

看看这里

于 2013-07-25T06:40:28.363 回答
2

至于嵌套循环,我发现有用的是创建一个交叉列表。例如

`对于 i=1:10

 for j=1:20
    for k=1:30
      f(i, j, k)

`

在 q 你可以

il: 1 _til 11
jl: 1_til 21
kl: 1_til 31
lst: il cross jl cross kl
raze g(x) each til count ls

其中 g 定义为

g: {[i]
itr: first lst[i];
jtr: first 1_lst[i];
ktr: last lst[i];

f(itr, jtr, ktr)
}

希望这可以澄清。至于闭包部分不知道那里的 R 语法。如果你能说出你想要什么输出,可以提供帮助。

于 2013-05-24T12:23:14.923 回答
2

一种方法是计算您关心的索引,然后索引到数组中 -

q) f:{y til[x]+/:neg[x]_til count y} // [x] = sublist length [y] = list
q) f[3;18.54 18.53 18.53 18.52 18.57 18.9 18.9 18.77 18.59 18.51 18.37]
    18.54 18.53 18.53
    18.53 18.53 18.52
    18.53 18.52 18.57
    18.52 18.57 18.9
    18.57 18.9  18.9
    18.9  18.9  18.77
    18.9  18.77 18.59
    18.77 18.59 18.51
于 2013-05-24T17:38:39.137 回答
2

Q 确实有传统的循环结构:do重复一个语句给定的次数,while重复一个给定的条件。此外, q “副词” eachover等让您“循环”函数在向量的元素上。

但是,要充分利用 q 的强大功能,您应该学习如何避免显式循环。

简化您的特定示例(使用简单的向量而不是 1 列表),以下是重新组合价格向量的方法:

q)price:18.54 18.53 18.53 18.52 18.57 18.9 18.9 18.77 18.59 18.51 18.37
q)2_flip(prev x;x:prev x;x:price)
18.54 18.53 18.53
18.53 18.53 18.52
18.53 18.52 18.57
18.52 18.57 18.9
18.57 18.9  18.9
18.9  18.9  18.77
18.9  18.77 18.59
18.77 18.59 18.51
18.59 18.51 18.37

下面是它的工作原理。(记住:q 从右到左计算。)prev函数将价格在向量中向右移动(在开头添加空值并丢弃最后一个值:

q)prev price
0n 18.54 18.53 18.53 18.52 18.57 18.9 18.9 18.77 18.59 18.51
q)prev prev price
0n 0n 18.54 18.53 18.53 18.52 18.57 18.9 18.9 18.77 18.59

我们将原始价格向量与 $3\times n$ 矩阵中的两个移位相结合。这个矩阵的列是我们想要的组。翻转函数转置矩阵,以便您将组作为行,最后我们使用_运算符删除包含空值的前两行。

于 2015-10-03T00:43:05.540 回答
0

就像上面的一些用户建议的那样,我认为最好的办法是计算你想要的指数。如果可能,请避免使用 lambda 并使用运算符(这是更好的风格)。

 q)v:18.54 18.53 18.53 18.52 18.57 18.9 18.9 18.77 18.59 18.51 18.37
 q)v (0 1 2)+/:til count v ///generate a list of indices from 0 to the count of v, then add 0 1 2 to each of those indices
18.54 18.53 18.53
18.53 18.53 18.52
18.53 18.52 18.57
18.52 18.57 18.9
18.57 18.9  18.9
18.9  18.9  18.77
18.9  18.77 18.59
18.77 18.59 18.51
18.59 18.51 18.37
18.51 18.37
18.37
于 2013-08-13T20:52:58.160 回答