我一直在使用ReactiveCocoa,我遇到了一个有趣的问题。我可以设想任何数量的丑陋,有状态的解决方案,但我非常有信心,有一种优雅,功能性的方式,无论出于何种原因,我的脑海中都没有实现。也许你可以帮忙!
此处的输入信号是两部分字符串,如"<letter>,<number>"
。所需的排序规则是,对于给定的字母,输入值应按<number>
的顺序出现在输出中(即A,2
永远不会出现在A,1
之前)和所有字母中的输出值<letter>
输出不应违反alpha顺序。 (即,在B
以至少一个字符串出现之前,不会出现以A
开头的字符串。)除了这些规则所规定的,期望事情将按顺序到达输出他们被提交给输入。
请考虑以下代码:
RACSubject* input = [RACSubject subject];
RACSignal* output = [input <SOME CHAIN OF SIGNAL FUNCTIONS>];
[output subscribeNext:^(id x) { NSLog(@"(%@)",x); }];
[input sendNext: @"A,2"]; // Expect no output
[input sendNext: @"B,4"]; // Expect no output
[input sendNext: @"B,2"]; // Expect no output
[input sendNext: @"B,1"]; // Expect no output
[input sendNext: @"A,1"]; // Expect output: (A,1) (A,2) (B,1) (B,2)
// Note: (A,1) (B,1) (B,2) (A,2) would *not* be right because A,2 appeared on the input before B,1
[input sendNext: @"C,1"]; // Expect output: (C,1)
[input sendNext: @"B,3"]; // Expect output: (B,3) (B,4)
[input sendNext: @"C,3"]; // Expect no output
[input sendNext: @"C,2"]; // Expect output: (C,2) (C,3)
也应该“急切地”产生输出。如果我必须等到输入信号完成才能看到输出(当然,除非排序规则规定是这种情况,即如果A,1
最后出现的话),这没有用。
有什么想法吗?
答案 0 :(得分:5)
以命令式方式编写,你可能会使用一些累加器变量,然后循环输入值并根据需要操作累加器。
函数式编程中最接近的并行是 scan (在ReactiveCocoa中表示为-scanWithStart:reduce:
)。扫描允许您通过流“线程化”状态,并在新输入值到达时使用它。
结果看起来非常类似于命令式累积,除了任何突变都不会逃离扫描块:
RACSignal *output = [[[[input
map:^(NSString *combo) {
NSArray *components = [combo componentsSeparatedByString:@","];
NSInteger number = [components[1] integerValue];
return RACTuplePack(components[0], @(number));
}]
// We need four state parameters:
// 1. The letter we're waiting for.
// 2. The number we're waiting for.
// 3. Values received that cannot be forwarded until a certain
// letter/number.
// 4. The values to forward at each step.
scanWithStart:RACTuplePack(@"A", @1, @[], @[]) reduce:^(RACTuple *state, RACTuple *letterAndNumber) {
NSString *waitingLetter = state[0];
NSNumber *waitingNumber = state[1];
NSArray *queuedValues = state[2];
// Enqueue this value until we're ready to send it (which may or may not
// occur on this step of the scan).
queuedValues = [queuedValues arrayByAddingObject:letterAndNumber];
if ([letterAndNumber.first isEqual:waitingLetter] && [letterAndNumber.second isEqual:waitingNumber]) {
// Determine the next letter and number.
waitingLetter = …;
waitingNumber = @(waitingNumber.integerValue + 1);
// Sort queuedValues lexically and numerically.
NSArray *forwardValues = …;
// We should no longer have any values queued, since we want to
// forward them all.
return RACTuplePack(waitingLetter, waitingNumber, @[], forwardValues);
} else {
// No values should escape the scan yet. Just pass on our queued
// values.
return RACTuplePack(waitingLetter, waitingNumber, queuedValues, @[]);
}
}]
map:^(RACTuple *state) {
// Convert the array of values into a signal.
NSArray *forwardValues = state.last;
return forwardValues.rac_sequence.signal;
}]
// Forward values from each inner signal in the correct, sorted order.
concat];
为简洁起见,我省略了一些排序逻辑,但很容易填写算法的确切细节。
答案 1 :(得分:1)
正如问题所说(A,1) (A,2) (B,1) (B,2) (C,1) (B,3) (B,4)
是有效的输出,我们会发现当您查看(C,1)
之间的(B,2)
时,我们需要存储每个字母的等待数字。 (B,3)
。
更重要的是,正如问题所说:
除了这些规则所规定的,期望就是事情 将按照提交给他们的顺序到达输出 输入
因此在转发一些值后,我们可能仍会有一些排队的值。以问题的输出为例,转发(B,4)
后,我们仍必须排队等待(B,3)
。{/ p>
这是一个代码示例,RAC部分与Justin的几乎相同,而我强调了评论中的差异:
另外,我发布了一个完整且可运行的代码示例:http://d.pr/X59S/9p9bT58U。
(C,1)