天道酬勤,学无止境

How to build a Map that replicates a Function in Java's Lambda API

From a java.util.function.BiFunction that maps a pair of Enums into a value, I want to build a EnumMap that reflects that mapping.

For instance, let E1 and E2 be enum types and T any given type:

 BiFunction<E1,E2, T> theBiFunction = //...anything

 EnumMap<E1,EnumMap<E2,T>> theMap = 
    buildTheMap(                     // <--  this is where the magic happens
                E1.values(), 
                E2.values(),
                theBiFunction);

Given any pair of values of type E1 and E2

E1 e1 = //any valid value...
E2 e2 = //any valid value....

both values below should be equal:

T valueFromTheMaps = theMap.get(e1).get(e2);
T valueFromTheFunction = theBiFunction.apply(e1,e2);

boolean alwaysTrue = valueFromTheMaps.equals(valueFromTheFunction);

What's the best (more elegant, efficient, etc...) implementation for the method where the "magic" takes place?

评论

You get an elegant solution if you go to a generic solution and break it down. First, implement a generic function which creates an EnumMap out of a Function, then implement the nested mapping of a BiFunction using the first function combined with itself:

static <T,E extends Enum<E>>
  EnumMap<E,T> funcToMap(Function<E,T> f, Class<E> t, E... values) {
    return Stream.of(values)
      .collect(Collectors.toMap(Function.identity(), f, (x,y)->x, ()-> new EnumMap<>(t)));
}
static <T,E1 extends Enum<E1>,E2 extends Enum<E2>>
  EnumMap<E1,EnumMap<E2,T>> biFuncToMap(
  BiFunction<E1,E2,T> f, Class<E1> t1, Class<E2> t2, E1[] values1, E2[] values2){

  return funcToMap(e1->funcToMap(e2->f.apply(e1, e2), t2, values2), t1, values1);
}

Here’s a little test case:

enum Fruit {
    APPLE, PEAR
}
enum Color {
    RED, GREED, YELLOW
}

EnumMap<Fruit, EnumMap<Color, String>> result
  =biFuncToMap((a,b)->b+" "+a,
     Fruit.class, Color.class, Fruit.values(), Color.values());
System.out.println(result);

{APPLE={RED=RED APPLE, GREED=GREED APPLE, YELLOW=YELLOW APPLE}, PEAR={RED=RED PEAR, GREED=GREED PEAR, YELLOW=YELLOW PEAR}}

Of course, using the generic solution you can built methods for concrete enum types which do not require the Class parameter(s)…


This ought to work smoothly with a parallel stream if the provided (Bi)Function is thread safe.

As a baseline for comparison, here's the conventional version:

<T> EnumMap<E1,EnumMap<E2,T>> buildTheMap(E1[] e1values,
                                          E2[] e2values,
                                          BiFunction<E1,E2,T> f) {
    EnumMap<E1,EnumMap<E2,T>> outer = new EnumMap<>(E1.class);
    for (E1 e1 : e1values) {
        EnumMap<E2,T> inner = new EnumMap<>(E2.class);
        for (E2 e2 : e2values) {
            inner.put(e2, f.apply(e1, e2));
        }
        outer.put(e1, inner);
    }
    return outer;
}

Now here's a version that uses nested three-arg forms of the collect() stream terminal operation:

<T> EnumMap<E1,EnumMap<E2,T>> buildTheMap(E1[] e1values,
                                          E2[] e2values,
                                          BiFunction<E1,E2,T> f) {
    return
        Stream.of(e1values)
              .collect(() -> new EnumMap<>(E1.class),
                       (map, e1) -> map.put(e1, Stream.of(e2values)
                                                      .collect(() -> new EnumMap<>(E2.class),
                                                               (m, e2) -> m.put(e2, f.apply(e1, e2)),
                                                               Map::putAll)),
                       Map::putAll);
}

What makes this cumbersome is that the accumulator function for the outer collector has to run a stream with its own three-arg collector to produce the inner map. This is really hard to indent well. Instead of standard spacing, I've lined up the three arguments to each collect() call. This makes it pretty wide, but if I didn't do this, it would be hard to see what goes with what since the nesting is so deep. As much of a fan of streams as I am, it's hard for me to say that this is any better than the conventional version.

You might say, "Why not use toMap() instead of the three-arg collect() function?" The problem is that we need to create EnumMap instances, and the overload of toMap() that takes a map supplier has four arguments:

toMap(keyFunc, valueFunc, mergeFunc, mapSupplier)

Worse, the merge function (third arg) isn't used, so we'd have to supply a function that's never used. Here's what that looks like:

<T> EnumMap<E1,EnumMap<E2,T>> buildTheMap(E1[] e1values,
                                          E2[] e2values,
                                          BiFunction<E1,E2,T> f) {
    return
        Stream.of(e1values)
              .collect(toMap(e1 -> e1,
                             e1 -> Stream.of(e2values)
                                         .collect(toMap(e2 -> e2,
                                                        e2 -> f.apply(e1, e2),
                                                        (x, y) -> x,
                                                        () -> new EnumMap<>(E2.class))),
                             (x, y) -> x,
                             () -> new EnumMap<>(E1.class)));
}

Doesn't look any better to me. My money is still on the conventional version.

There are a number of alternative approaches one could try. We'll see what a good night's sleep brings.

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • Spark 学习笔记
    Spark 学习笔记 Spark Spark介绍 Spark安装Spark架构、角色Spark任务执行流程Spark World Count Spark RDD 什么是 RDDRDD 的属性创建 RDD 从内存从文件RDD 分区RDD 的编程 API RDD 依赖关系 DAG 的生成 RDD 的持久化 RDD 的 cache(持久化)如何使用 Spark 累加器和广播变量 Spark SQL DataFrame RDD 转换成 DataFrame Datasets SparkStreaming 什么是Spark Streaming Spark Straming如何工作Spark Streaming 优缺点 示例应用程序 数据投递程序数据消费程序任务提交到Spark集群结果输出 参考链接 Spark Spark介绍 Spark安装 https://juejin.im/post/5d02365ce51d45109725fe65 Spark架构、角色 Spark架构使用了分布式计算中master-slave模型,master是集群中含有master进程的节点,slave是集群中含有worker进程的节点SparkContext: Spark的主要入口点,代表对计算集群的一个连接,是整个应用的上下文,负责与ClusterManager通信,进行资源申请、任务的分配和监控等Driver
  • Tensorflow 数据集 API 中的过采样功能(Oversampling functionality in Tensorflow dataset API)
    问题 我想问一下当前数据集的API是否允许实现过采样算法? 我处理高度不平衡的阶级问题。 我认为在数据集解析(即在线生成)期间对特定类进行过采样会很好。 我已经看到了 reject_resample 函数的实现,但是这会删除样本而不是复制它们,并且它会减慢批处理的生成速度(当目标分布与初始分布大不相同时)。 我想实现的是:举个例子,看它的类概率决定是否复制它。 然后调用dataset.shuffle(...) dataset.batch(...)并获取迭代器。 最好的(在我看来)方法是对低概率类别进行过采样,并对最可能的类别进行二次采样。 我想在线进行,因为它更灵活。 回答1 此问题已在问题 #14451 中解决。 只需在此处发布 anwser 即可使其对其他开发人员更可见。 示例代码对低频类进行过采样,对高频类进行欠采样,其中class_target_prob在我的情况下只是均匀分布。 我想从最近的手稿中检查一些结论 A System study of the class didn't problem in Convolutional Neural Networks 特定类的过采样是通过调用完成的: dataset = dataset.flat_map( lambda x: tf.data.Dataset.from_tensors(x).repeat(oversample
  • ​闷棍暴打面试官 JDK源码系列 (一) 打破 lambda 问到底 !
    家喻户晓的 lambda Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。 Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。Stream API −新添加的Stream API(java.util.stream) 把真正的Lambda函数式编程风格引入到Java中。 更多请参考 https://www.runoob.com/java/java8-new-features.html 我们在学习基于lambda 开发的众多 api 的时候一定要弄清楚的一个问题. lambda 语法与基于lambda 语法的api类之间到底有什么关系! 首先看看 lambda 风格代码 @FunctionalInterface interface MathOperation { String sayMessage(Integer b); } // lambda 原生函数风格 public String lambdaApi(Integer data){ String salutation = "Hello lambda"; MathOperation
  • 分布式缓存的实战1
    红帽最新的runtime与红帽data grid 8.0一起发布,该版本提供了分布式的内存中NoSQL数据存储解决方案。您的应用程序可以以内存速度访问,处理和分析数据,以提供卓越的用户体验。。数据网格包括Infinispan开源软件项目。它可以部署为嵌入式库、独立服务器或Red Hat OpenShift容器平台上的容器化应用程序。接下来,我们查看几个红帽Data Grid的应用场景。您可能想使用Cache的情况是什么?让我们花点时间考虑一下。您认为可以在哪里使用缓存?那么这个问题可能会有无限的答案。下面列出了一些常见的用例查找数据:如果您有一个应用程序,则需要一些用户配置文件数据等,则不必每次都挂起,并且它的变化不会太大延迟或批量:您可能有一个需要花费很多时间来加载某些数据的服务或数据库。Traffic:您可能有大量的用户,趋势正在激增Session存储;存储您的webapp会话(可能是购物车等),可用于扩展应用程序Global Counters:您可能要跨分布式数据集创建分布式键。使用它来更新和获取数据。场景1:创建本地缓存首先,数据网格适合地图应用。为什么地图适合缓存?地图速度很快,它们使用诸如hashcode()之类的方法,并且等于确定如何向地图添加数据。这也意味着它们可以快速读取和写入数据。这正是缓存所期望的。数据存储在键和值对中。Google Maps还有更多功能
  • 在未指定类型参数的情况下无法将Java 8方法与lambda参数一起使用(Cannot use Java 8 method with lambda arguments without specifying type arguments)
    问题 我制作了一个带有类型实参的方法,使用这些类型实参返回一个泛型,并采用了也依赖于类型实参的Function参数。 当我使用lambda作为参数时,编译器会强迫我指定方法的类型参数,这感觉不对。 我正在设计一个实用程序类,该类具有与Stream.flatMap一起使用的方法。 它将每种集合条目映射到包含键和值元素的FlatEntry,并且可以使用构建器在多个级别上执行此操作。 受影响的方法是flatEntryMapperBuilder 。 这是代码: import java.util.function.Function; import java.util.stream.Stream; public class GdkStreams { public static <T, K, V> Function<T, Stream<FlatEntry<K, V>>> flatEntryMapper(Function<T, K> keyMapper, Function<T, Stream<V>> valueMapper) { return input -> { K key = keyMapper.apply(input); return valueMapper.apply(input).map(value -> new FlatEntry<>(key, value)); }; } public
  • Reactive 架构才是未来
    简介: Reactive 编程模型有哪些价值?它的原理是什么?如何正确使用?本文作者将根据他学习和使用的经历,分享 Reactive 的概念、规范、价值和原理。欢迎同学们共同探讨、斧正。 Reactive 和 Reactive programming Reactive 直接翻译的意思式反应式,反应性。咋一看,似乎不太好懂。 举个例子:在 Excel 里,C 单元格上设置函数 Sum(A+B),当你改变单元格 A 或者单元格 B 的数值时,单元格 C 的值同时也会发生变化。这种行为就是 Reactive。 在计算机编程领域,Reactive 一般指的是 Reactive programming。指的是一种面向数据流并传播事件的异步编程范式(asynchronous programming paradigm)。 先举个例子大家感受一下: public static void main(String[] args) { FluxProcessor<Integer, Integer> publisher = UnicastProcessor.create(); publisher.doOnNext(event -> System.out.println("receive event: " + event)).subscribe(); publisher.onNext(1); //
  • pytorch分布式训练(一):torch.nn.DataParallel
      本文介绍最简单的pytorch分布式训练方法:使用torch.nn.DataParallel这个API来实现分布式训练。环境为单机多gpu,不妨假设有4个可用的gpu。 一、构建方法 使用这个API实现分布式训练的步骤非常简单,总共分为3步骤: 1、创建一个model,并将该model推到某个gpu上(这个gpu也将作为output_device,后面具体解释含义),不妨假设推到第0号gpu上, device = torch.device("cuda:0") model.to(device) 2、将数据推到output_device对应的gpu上, data = data.to(device) 3、使用torch.nn.DataParallel这个API来在0,1,2,3四个gpu上构建分布式模型, model = torch.nn.DataParallel(model, device_ids=[0,1,2,3], output_device=0) 然后这个model就可以像普通的单gpu上的模型一样开始训练了。 二、原理详解 2.1 原理图   首先通过图来看一下这个最简单的分布式训练API的工作原理,然后结合代码详细阐述。 将模型和数据推入output_device(也就是0号)gpu上。 0号gpu将当前模型在其他几个gpu上进行复制,同步模型的parameter
  • AWS Lambda函数可以调用另一个(Can an AWS Lambda function call another)
    问题 我有2个Lambda函数-一个产生报价,另一个将报价变成订单。 我希望Order lambda函数调用Quote函数来重新生成报价,而不是仅仅从不受信任的客户端接收报价。 我看过我能想到的任何地方-但看不到如何链接或调用函数...肯定存在! 回答1 我找到了使用aws-sdk 。 var aws = require('aws-sdk'); var lambda = new aws.Lambda({ region: 'us-west-2' //change to your region }); lambda.invoke({ FunctionName: 'name_of_your_lambda_function', Payload: JSON.stringify(event, null, 2) // pass params }, function(error, data) { if (error) { context.done('error', error); } if(data.Payload){ context.succeed(data.Payload) } }); 您可以在以下位置找到该文档:http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html 回答2 您应该通过SNS链接Lambda
  • 如何使用 lambda 初始化地图?(How to initialize a map using a lambda?)
    问题 我想在单个语句(可能包含多个嵌套语句)中声明一个完全填充的地图字段,如下所示: private static final Map<Integer,Boolean> map = something-returning-an-unmodifiable-fully-populated-HashMap; 匿名初始化程序不会这样做,原因与调用返回新填充映射的函数不会这样做的原因相同:它们需要两个顶级语句:一个用于变量声明,另一个用于方法或初始化程序。 双花括号( {{和}} )习语会起作用,但它创建了一个扩展HashMap<>全新类,我不喜欢由此表示的开销。 Java 8 的 lambda 表达式是否提供了更好的方法来实现这一点? 回答1 如果要在单个语句中初始化Map ,可以使用 Collectors.toMap。 想象一下,您想要构建一个Map<Integer, Boolean>将整数映射到调用某个函数f的结果: private static final Map<Integer,Boolean> MAP = Collections.unmodifiableMap(IntStream.range(0, 1000) .boxed() .collect(Collectors.toMap(i -> i, i -> f(i)))); private static final boolean
  • AWS Lambda:无法删除 arn,因为它是一个复制函数(AWS Lambda: unable to delete arn because it is a replicated function)
    问题 我正在尝试通过 GUI 删除 AWS Lambda 函数,但收到响应: There was an error deleting your function: Lambda was unable to delete arn:aws:lambda:us-east-1:624929674184:function:lambda-auth:1 because it is a replicated function. 如何删除复制的 Lambda 函数? 回答1 我已经找到了删除 Lambda@edge 副本的解决方案。 首先,登录到 CloudFront 控制台并转到您的Distribution 。 在行为选项卡下 - 勾选列出的行为并编辑向下滚动到 Lambda 函数关联并通过单击X删除任何关联。 按是,编辑以保存更改。 --- 现在您已经删除了关联,是时候删除 Lambda@edge 副本了转到 Lambda 控制台并打开您的 lambda(您希望删除)。 在顶部菜单 -限定符->版本-> 选择列出的下拉版本它将打开@edgeLambda 版本在顶部菜单 -操作->删除版本这样,删除所有版本 - 您只剩下$LATEST 也删除它 - 您终于可以删除 Lambda@edge 函数 注意!> 请记住删除与 Lambda@edge 函数关联的任何 IAM 角色和权限。 我希望这会奏效:)
  • Cannot use Java 8 method with lambda arguments without specifying type arguments
    I made a method with type arguments, returning a generic type using these type arguments, and taking Function arguments which also depends on the type arguments. When I use lambdas as arguments, the compiler forces me to specify the type arguments of the method, which feels wrong. I am designing a utility class with methods to use with Stream.flatMap. It maps every kind of collection entry to a FlatEntry which contains a key and value element, and can do this on multiple levels with a builder. The affected method is flatEntryMapperBuilder. Here is the code: import java.util.function.Function
  • 在 lambda 函数中解包的值太多(Too many values to unpack in lambda function)
    问题 我刚开始学习 Python。 我正在使用 API 来构建 TFIDF 模型,但是我遇到了一些无法解决的 lambda 函数错误。 这是生成 TFIDF 的类的一部分: class tfidf(ModelBuilder, Model): def __init__(self, max_ngram=1, normalize = True): self.max_ngram = max_ngram self.normalize = normalize def build(self, mentions, idfs): m = mentions\ .map(lambda (target, (span, text)): (target, text))\ """error is triggered here """ .mapValues(lambda v: ngrams(v, self.max_ngram))\ .flatMap(lambda (target, tokens): (((target, t), 1) for t in tokens))\ .reduceByKey(add)\ .map(lambda ((target, token), count): (token, (target, count)))\ .leftOuterJoin(idfs)\ 这是mentions类的示例输出
  • 如何将查询字符串和标头映射到 AWS C# lambda 函数参数(How to map querystring and header to AWS C# lambda function parameter)
    问题 我有 AWS Gateway REST API,它采用 2 个查询字符串参数 https://xxxxxx.xxxx.us-east-1.amazonaws.com/dev/pets?type=dog&page=1 API 的调用者还在标头中包含x-api-key 。 我希望 API 网关将查询字符串参数和x-api-key传递给 lambda 函数。 因此,在AWS API Gateway Console我已将Integration Request配置如下 lambda 函数看起来像这样 namespace AWSLambda1 { public class Function { public string FunctionHandler(LambdaRequest request, ILambdaContext context) { return string.Format("{0},{1},{2}", request.Type, request.Page, request.ApiKey); } } } public class LambdaRequest { public string Type { get; set; } public string Page { get; set; } public string ApiKey { get; set; } } 问题 1
  • Too many values to unpack in lambda function
    I just started learning Python. I'm using an API to build a TFIDFs model, however I'm facing some errors with the lambda functions which I cannot resolve. This is part of the class that generates the TFIDFs: class tfidf(ModelBuilder, Model): def __init__(self, max_ngram=1, normalize = True): self.max_ngram = max_ngram self.normalize = normalize def build(self, mentions, idfs): m = mentions\ .map(lambda (target, (span, text)): (target, text))\ """error is triggered here """ .mapValues(lambda v: ngrams(v, self.max_ngram))\ .flatMap(lambda (target, tokens): (((target, t), 1) for t in tokens))\
  • 如何将 AWS API Gateway 查询字符串映射到 C# AWS Lambda 函数?(How do I map AWS API Gateway query string to C# AWS Lambda function?)
    问题 我有一个使用 GET 请求从 API 网关调用的 C# lambda 函数。 [LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] public ResponseModel MyFunction(RequestModel request) { return new ResponseModel { body = "Hello world!" }; } public class RequestModel { [JsonProperty("a")] public string A { get; set; } [JsonProperty("b")] public string B { get; set; } } public class ResponseModel { public int statusCode { get; set; } = 200; public object headers { get; set; } = new object(); public string body { get; set; } = ""; } 如何将发送到 API 网关的查询字符串参数映射到MyFunction的RequestModel参数? 我已经用参数调用了该函数,但它们似乎没有通过。
  • 授予 AWS Api Gateway 使用 BOTO3 调用 Lambda 函数的权限(Giving AWS Api Gateway Permission To Invoke Lambda Function using BOTO3)
    问题 我正在尝试使用 BOTO3 创建一个调用 lambda 函数的 Api 网关方法。 到目前为止,我一直无法找到如何授予必要的权限。 奇怪的是,通过 AWS 控制台手动设置 lambda 方法名称会自动设置权限。 我一直无法在代码中复制这一点。 这是我用来设置网关的代码: # Create a rest api self.rest_api = self.apigateway.create_rest_api( name='AWS_CMS_Operations' ) # Get the rest api's root id root_id = self.apigateway.get_resources( restApiId=self.rest_api['id'] )['items'][0]['id'] # Create an api resource api_resource = self.apigateway.create_resource( restApiId=self.rest_api['id'], parentId=root_id, pathPart='AWS_CMS_Manager' ) # Add a post method to the rest api resource api_method = self.apigateway.put_method(
  • 是否可以将 Socket.io 与 AWS Lambda 一起使用?(Is it possible to use Socket.io with AWS Lambda?)
    问题 是否可以在 AWS Lambda 中构建一个函数来创建 websocket 并将数据发送到订阅的应用程序? 像这样的东西: John 在他的手机中打开了 SuperPhotoApp 应用程序,但决定使用桌面浏览器将照片上传到 SuperPhotoApp 服务(一个 S3 存储桶),此事件执行一个 Lambda 函数,该函数创建一个 socket.io 服务器并将更新推送给所有订阅者,他的手机打开了应用程序,因此应用程序会自动更新新照片。 这可以通过推送通知或 Amazon SNS 来完成,但是如果我需要实时行为,例如需要更新角色位置的在线游戏,该怎么办。 如果 Lambda 无法做到这一点,是否有任何解决方案可以让我使用桌面浏览器更新我打开的应用程序? Amazon EC2 是唯一的选择吗? 我读过它在缩放方面存在问题,这就是我评论 Lambda 的原因。 回答1 我认为 Lambda 不会适用于您在此处描述的情况。 下面 AWS 论坛的链接指出,Lambda 函数最多只能运行 15 分钟,而且由于您每 100 毫秒的函数运行时收费,这可能会导致成本过高。 亚马逊发表评论说,他们已经多次听到这个请求,因此对某种方式感兴趣以允许这样做。 https://forums.aws.amazon.com/thread.jspa?threadID=205761 这是一个人的帖子
  • Giving AWS Api Gateway Permission To Invoke Lambda Function using BOTO3
    I am attempting to use BOTO3 to create an Api Gateway method that invokes a lambda function. I have so far been unable to find how to grant the necessary permissions. Curiously, setting the lambda method name manually through the AWS console sets up permissions automatically. I have been unable to replicate this in code. This is the code I am using to set up the gateway: # Create a rest api self.rest_api = self.apigateway.create_rest_api( name='AWS_CMS_Operations' ) # Get the rest api's root id root_id = self.apigateway.get_resources( restApiId=self.rest_api['id'] )['items'][0]['id'] # Create
  • Haskell 函数参数的顺序有没有意义?(Is there significance in the order of Haskell function parameters?)
    问题 我一直在学习 Haskell,我注意到许多内置函数接受参数的顺序与我所期望的相反。 例如: replicate :: Int -> a -> [a] 如果我想复制 7 两次,我会写replicate 2 7 。 但是当用英语大声朗读时,函数调用感觉就像在说“复制 2、7 次”。 如果我自己编写函数,我会交换第一个和第二个参数,以便replicate 7 2将读取“复制 7, 2 次”。 在我处理 99 Haskell 问题时出现了一些其他示例。 我不得不写一个函数: dropEvery :: [a] -> Int -> [a]` 它接受一个列表作为它的第一个参数,一个Int作为它的第二个参数。 直观地说,我会将标题写为dropEvery :: Int -> [a] -> [a]以便dropEvery 3 [1..100]将读作:“删除列表中的每三个元素[1..100] . 但在问题的例子中,它看起来像: dropEvery [1..100] 3 。 我也看到了我现在找不到的其他功能。 由于实际原因,以这种方式编写函数是否很常见,还是只是在我的脑海中? 回答1 函数以这种方式编写的原因之一是因为它们的柯里化形式被证明是有用的。 例如,考虑函数map和filter : map :: (a -> b) -> [a] -> [b] filter :: (a -> Bool) ->
  • 什么是反应式编程? 这里有你想要了解的反应式编程 (Reactive programming)
    理解反应式编程 你曾有过订阅报纸或者杂志的经历吗?互联网的确从传统的出版发行商那儿分得了一杯羹,但是过去订阅报纸真的是我们了解时事的最佳方式。那时,我们每天早上都会收到一份新鲜出炉的报纸,并在早饭时间或上班路上阅读。现在假设一下,在支付完订阅费用之后,几天的时间过去了,你却没有收到任何报纸。又过了几天,你打电话给报社的销售部门询问为什么还没有收到报纸。 想象一下,如果他们告诉你:“因为你支付的是一整年的订阅费用,而现在这一年还没有结束,当这一年结束时,你肯定可以一次性完整地收到它们。”那么你会有多么惊讶。值得庆幸的是,这并非订阅的真正运作方式。报纸具有一定的时效性。在出版后,报纸需要及时投递,以确保在阅读它们时内容仍然是新鲜的。此外,当你在阅读最新一期的报纸时,记者们正在为未来的版本撰写内容,同时印刷机正在满速运转,印刷下一期的内容——一切都是并行的。在开发应用程序代码时,我们可以编写两种风格的代码,即命令式和反应式。 •命令式(Imperative)的代码:非常类似于上文所提的虚构的报纸订阅方式。它由一组任务组成,每次只运行一项任务,每项任务又都依赖于前面的任务。数据会按批次进行处理,在前一项任务还没有完成对当前数据批次的处理时,不能将这些数据递交给下一项处理任务。 •反应式(Reactive)的代码:非常类似于真实的报纸订阅方式。它定义了一组用来处理数据的任务