我想开一个关于Clojure的会议。你能推荐一个可以通过Clojure函数编程优雅解决的问题吗?你能指出涵盖这个主题的资源吗?
答案 0 :(得分:12)
使用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
,socialSecurityNumber
和其他字段。您正在创建一个Web服务,用于检索数据库中所有人员的姓名和电子邮件。您不希望公开您的域,因此您创建了一个包含PersonDto
和name
的班次email
。这很简单,所以现在您需要一个函数来将数据从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
null(坏主意)或创建一个新的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使用这样的代码创造奇迹。
答案 1 :(得分:5)
经典并发问题“The Sleeping Barber”可能会很好。
以下是几个例子
http://www.bestinclass.dk/index.clj/2009/09/scala-vs-clojure-round-2-concurrency.html
https://github.com/bitsai/clojure-actors/blob/master/sleeping_barber.clj
答案 2 :(得分:1)
几个月前,我遇到了同样的问题,我们决定解决Clojure中的“蒙娜丽莎”问题。结果是this presentation。基本上我们表明Clojure对于解决“代码为数据”提供优雅解决方案的问题非常酷。例如,在遗传算法中。