只是踢了Java 8的轮胎,并注意到Lamdas和流功能编程。
想知道一个简单的命令行args消费者是否可以使用流。
无法弄清楚如何同时获得两个流元素然而...... 除此之外我还需要处理那些不会接受值的args,所以不能做任何奇怪/偶数的诡计。会考虑args始终以破折号开头,而可选值永远不会。
String[] args = ("-v", "-c", "myconfigfile", "-o", "outputfile");
Arrays.toList(args).stream().map( a,v -> evalArg(a,v));
public static void evalArg(String arg, String val) {
switch(arg) {
case "-v":
verbose = true;
break;
case "-c":
config_file = val;
break;
case "-o":
output_file = val;
break;
default:
System.err.println("unknown argument " + arg + " " + val);
break;
}
}
答案 0 :(得分:2)
如果您有键值对,则可以使用以下内容:
public static void main(final String[] args) {
String[] args = {"-v", "value", "-c", "myconfigfile", "-o", "outputfile"};
pairStream(Arrays.asList(args), (param, value) -> param + ": " + value)
.forEach(System.out::println);
}
public static <X, Y> Stream<Y> pairStream(List<X> list, BiFunction<X, X, Y> mapper) {
Supplier<X> s = list.iterator()::next;
return Stream.generate(() -> mapper.apply(s.get(), s.get()))
.limit(list.size() / 2);
}
// Result:
// -v: value
// -c: myconfigfile
// -o: outputfile
答案 1 :(得分:1)
Stream API实际上并不是为了处理依赖于另一个值的状态的值而构建的。使用的lambda函数应该是无状态的(另请参阅Java文档:Stream#map()
)。但是,可以在流上使用sequential()
来确保正确的顺序,并允许在用于处理元素的函数内部使用状态,但这并不是真的建议。
更好地使用库进行参数解析,例如Apache Commons CLI。
答案 2 :(得分:0)
使用lambdas / mapping没有简单的方法。并非所有形式的迭代都适合映射单个lambda。
例如,对此的功能方法可能不会使用lambdas;它将使用递归,如果需要,每个步骤可选择弹出更多args。像(伪代码,忽略错误检查等):
Options[] parse(args : String[]):
if args.isEmpty:
return []
currentOpt, remainingArgs = args.pop()
case currentOpt of:
"-v":
return [ Verbose ] ++ parse(remainingArgs)
"-c":
configPath, remainingArgs = remainingArgs.pop()
return [ Config(configPath) ] ++ parse(remainingArgs)
"-o":
outputPath, remainingArgs = remainingArgs.pop()
return [ Output(outputPath) ] ++ parse(remainingArgs)
_:
return [ UnknownOption(currentOpt) ] ++ parse(remainingArgs)
答案 3 :(得分:0)
在写完我的原始答案之后,我意识到这可以使用reduce,例如:
String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).reduce((arg, val) -> {
switch (arg) {
case "-v":
verbose = true;
break;
case "-c":
configFile = val;
break;
case "-o":
outputFile = val;
break;
// Non-exhaustive
}
return val;
});
我的原始答案,使用状态和功能对象:
请记住,Java实际上并不支持第一类函数。当您使用lambda时,您实际上正在传递包含函数的对象。这些对象可以具有状态。你可以利用这个优势,所以你应该能够做到这样的事情:
String[] foo = {"-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
String last;
public void accept(String t) {
if (last == null) {
last = t;
} else {
System.out.println(last + " " + t);
last = null;
}
}
});
这是否是一个好主意是一个不同的考虑因素。请注意,使用forEachOrdered作为普通的forEach不保证按特定顺序通过列表。还要注意,map不适用于使用两个元素做一件事,因为映射函数必须接受一个参数并返回一个结果,从而导致输入流和输出流之间的一对一关系。
对于您的具体示例,您必须执行以下操作:
String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
String arg;
public void accept(String val) {
if (arg == null) {
arg = val;
} else if (t.startsWith("-")) {
System.out.println(arg);
arg = val;
} else {
System.out.println(arg + " " + val);
arg = null;
}
}
});
第三种选择当然是yshavit suggests,而根本不使用流:
LinkedList<String> argList = LinkedList<>(Arrays.asList(args));
while(!argList.isEmpty()) {
switch (argList.pop()) {
case "-v":
verbose = true;
break;
case "-c":
configFile = argList.pop();
break;
case "-o":
outputFile = argList.pop();
break;
default:
System.err.println("unknown argument " + arg + " " + val);
break;
}
}
答案 4 :(得分:0)
你似乎有这个想法,但我想我会用一些额外的数据扩展它。
数据流的概念可能比Java早;处理它们至少可以追溯到处理器架构。 CPU采用Von Neumann Architecture构建;它允许处理器维护指向内存不同部分的指针,并且基本上可以一直访问所有内存。常用的一些其他处理器(例如GPU)是流处理器。它们一次处理一个操作,除了偶尔(通常是可疑的)聪明的技巧之外,不知道流的任何其他部分。这样就可以实现最佳的并行处理,这正是为什么GPGPU可以对某些任务起到极大的效果,但在运行整个机器时却非常无效。
Streams API允许您在相同的情况下运行操作。一次操作多个项目的唯一方法是通过减少;这基本上是另一个流的构建。通过它,理论上,你当然可以扫描参数和启动选项;但由于潜在的冗余,这并不意味着它在实践中是最好的。