我一直在使用Reactive Cocoa,我遇到了一个有趣的问题.我可以设想任何数量的丑陋,有状态的解决方案,但我非常有信心有一种优雅,功能性的方式,无论出于何种原因,我的脑子里都没有实现
这里的输入信号是两部分字符串,例如“< letter>,< number>”.所需的排序规则是对于给定的字母,输入值应该以< number>的顺序出现在输出处. (即A,2应该永远不会出现在A,1之前)和所有字母之间的字母< letter>输出不应违反alpha顺序. (即,在出现至少一个以A开头的字符串之前,不应出现以B开头的字符串.)除了这些规则所规定的情况之外,期望事物将按照它们提交给输入的顺序到达输出.
请考虑以下代码:
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出现在最后),那么它是没有用的
有任何想法吗?
以命令式方式编写它,您可能会使用一些累加器变量,然后循环输入值并根据需要操作累加器.函数式编程中最接近的并行是扫描(在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];
为简洁起见,我省略了一些排序逻辑,但是很容易填写算法的确切细节.