- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想举办一个有关 Clojure 的 session 。您能否推荐一个可以使用 Clojure 函数式编程优雅解决的问题?您能指出涵盖该主题的资源吗?
最佳答案
很多关于使用 Clojure 的争论似乎都与其并发处理有关,但我在这里不会触及这个问题。
我将列出一些我每周都不得不用 Java 处理的问题,以及我如何在 Clojure 中解决这些问题。
不变性
在 Java 中实现不变性是非常非常困难的。除了遵循严格的编码实践之外,您还必须非常仔细地选择框架和库。另外一个副作用是,您要么编写大量代码来创建干净且可用的 API,要么只是强制客户端处理它。
final Person person = personDao.getById(id);
// I would like to "change" the person's email, but no setters... :(
在 Clojure 中,您基于不可变数据结构对数据进行建模,因此默认情况下所有对象都是不可变的,因此 Clojure 提供了对这些结构进行操作的强大函数。
(let [person (get-by-id person-dao id)
person-with-email (assoc person :email email)]
; Use person-with-email...
转化
在 Java 中,您有一个域类 Person
,其中包含字段 id
、name
、email
、社会安全号
等。您正在创建一个 Web 服务,用于检索数据库中所有人员的姓名和电子邮件。您不想公开您的域,因此创建了一个包含 name
和 email
的 PersonDto
类。这很简单,所以现在您需要一个函数将数据从 Person
映射到 PersonDto
。大概是这样的:
public class PersonPopulator {
public PersonDto toPersonDto(Person person) {
return new PersonDto(person.getName(), person.getEmail());
}
public List<PersonDto> toPersonDtos(List<Person> persons) {
List<PersonDto> personDtos = new ArrayList<PersonDto>();
for (Person person : persons) {
personDtos.add(toPersonDto(person));
}
return personDtos;
}
}
好吧,这还不错,但是如果您想在 DTO 中放入更多数据怎么办?嗯,toPersonDto
中的构造函数代码会增加一点,不用担心。如果有两种不同的用例,一种是上面的情况,另一种是我们只想发送电子邮件的情况,该怎么办?好吧,我们可以将 name
保留为空(坏主意)或创建一个新的 DTO,也许是 PersonWithEmailDto
。因此,我们将创建一个新类,一些用于填充数据的新方法......您可能知道这是怎么回事?
Clojure 是一种具有不可变数据结构的动态类型语言,允许我执行此操作:
(defn person-with-fields [person & fields]
(reduce #(assoc %1 %2 (get person %2)) {} fields))
(person-with-fields {:id 1
:name "John Doe"
:email "john.doe@gmail.com"
:ssn "1234567890"} :name :email)
; -> {:email "john.doe@gmail.com", :name "John Doe"}
并操纵人员列表:
(map #(person-with-fields % :name :email) persons)
向某人添加临时数据也很容易:
(assoc person :tweets tweets)
这不会破坏任何东西。在 Java 中,如果你的对象是不可变的,它们可能没有 setter ,所以你必须编写大量样板文件才能修改一个字段(new Person(oldPerson.getName(), oldPerson.getEmail(), tweets
)),或者创建一个全新的类。可变对象提供了一个很好的 API (oldPerson.setTweets(tweets)
),但难以测试和理解。
测试
许多 Java 代码都基于某种状态,即使不需要它。这意味着您可以模拟此状态,这通常意味着额外的样板文件,如果您没有在创建代码时考虑到可测试性,那么这会变得更加困难。另一方面,没有模拟的测试通常很慢,并且依赖于数据库访问或时间或其他肯定会不时失败的东西。
在编写 Clojure 代码时,我注意到我实际上并不需要那么多状态。几乎唯一的情况是当我从“外部”检索某些内容时,无论是数据库还是某些 Web 服务。
摘要
我的代码是一个管道,从一端获取一些数据,然后通过过滤、转换或与其他数据连接来更改该数据,直到到达管道的末端。在管道内部,不需要真正更改数据,但在很多情况下,强大的函数和不可变的数据结构很有用,这就是为什么 Clojure 使用这样的代码会产生奇迹。
关于clojure - Clojure 的良好展示是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5353691/
为什么该语言的名称是“Clojure”? 我用谷歌搜索了一下,在#clojure 中询问。到目前为止,还没有运气。 最佳答案 Rich Hickey(他是 Clojure 的设计者)对此的评论是 wi
我不明白为什么升级后会出现以下编译错误: Compiling addr-verify.core Exception in thread "main" java.lang.NoClassDefFound
我试图将从映射操作返回的(惰性)序列传递给另一个映射操作,以便我可以在第一个序列中查找元素。代码从文本文件(以行/列格式)解析一些足球装置,清理它,然后返回一张 map 。 这是代码: (ns fix
我想过滤一组,例如: (filter-set even? #{1 2 3 4 5}) ; => #{2 4} 如果我使用clojure.core/filter我得到一个不是集合的seq: (filte
(defn hi[](+ 5 6)) (hi) (defn hi[](+ 6 7)) (hi) 你好,我是 clojure 的新手。如上所述,我编写了两个具有相同名称的函数。我们可以在 cloj
我按照这个伪代码递归地将十进制转换为二进制。 findBinary(decimal) if (decimal == 0) binary = 0 else binar
我正在尝试学习 Clojure 并尝试定义这个简单的函数: user=> (defn triple [arg] (* 3 arg)) #'user/triple user=> (triple 1) 3
是->和 ->>宏只是为了使代码更具可读性还是它们还有其他特定功能? 最佳答案 线程优先( -> )和线程最后( ->> )是为了使代码更具可读性。但这已经很重要了! 它允许取消嵌套函数调用(示例取自
我在 http://www.learningclojure.com/2010/11/yet-another-way-to-write-factorial.html 上找到了这个代码,但我不明白 pop
我正在阅读 Programming Clojure 2nd edition,在第 49 页它涵盖了 Clojure 的 for 循环结构,它说它实际上是一个序列理解。 作者建议使用以下代码: (def
Clojure 中有双端队列吗?我的印象是 Clojure 的 PersistentQueue 是单端的(我错了吗?)。我需要能够从队列的任一端删除(即“pop”)和“peek”数据。我所说的双端队列
换句话说,有没有办法在看起来不像 (MACRO arg* ...) 的表单上触发宏扩展? . 举一个假设的例子: (defmacro my-var (do (printf "Using my-va
我很难理解懒惰。 有人能帮我理解为什么我下面的函数不是懒惰的吗 (defn my-red ([f coll] (my-red f (first coll) (rest coll) ))
在 Clojure 核心中决定参数函数顺序的规则是什么(如果有的话)? 类似 map 的函数和 filter期望数据结构作为最后一个 争论。 类似 assoc 的函数和 select-keys期待数据
我在 clojuredocs 上遇到过 completing 函数,但目前没有文档。 你能提供一些例子吗? 最佳答案 completing 用于扩充可能没有具有一元“完成”元数的一元重载的二元归约函数
这个现在支持吗?我能找到的唯一信息是来自维基的示例( https://github.com/clojure/core.match/wiki/Deftype-and-defrecord-matching
我正在关注“Clojure in Action”,对此我感到困惑: (defn with-log [function-to-call log-statement ] (fn [& args
对于下面的代码,箭头是宏还是函数名称中的简单字符? (来自 here) (defn file->map [file] ;; TODO ) 最佳答案 箭头是函数名称的一部分。有一个函数定义,不是
Clojure 的 range函数包含来自 start独家在end (如果提供)。核心库中是否有一个函数可以提供完全包含(开始和结束)的范围? 我发现在某些情况下必须调整最终值的代码 - 例如向下而不
当我尝试从 REPL 运行以下代码时(使用动态记录): (defrecord (symbol "rec2") (vec (map symbol ["f1" "f2"]))) 我收到错误 Compile
我是一名优秀的程序员,十分优秀!