想买个NEW DSL,新手闲鱼买家说商品有问题题求助

博客访问: 253430
博文数量: 86
博客积分: 1984
博客等级: 上尉
技术积分: 1003
注册时间:
学无所长,一事无成
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: Python/Ruby
参考文章:
几乎所有的 Ruby 新手都尝试过写一个 Ruby DSL。本文即引导你如何编写一个简单的 Ruby DSL。
领域专用语言 (DSL) 一般是用来描述特定的专用问题的程序或描述语言,它一般规模不大。DSL的受众是最终用户或者特定领域的使用者,他们往往都不是专业程序员。将 DSL 分成两类:外部的和内部的。一个外部的 DSL 同它本身的实现语言往往大相径庭,它需要通过其实现语言来进行解释或者翻译。而内部的DSL 往往是通过对实现语言进行变换伪装,使得它看上去像是某种领域专用语言。(我们这里的的 DSL 实现即是通过绑定变化 Ruby 语言来实现的。)
Ruby 代码块
Ruby 的代码块(即闭包)非常有用,可用来实现内部 DSL 。
Ruby 代码块 (其他语言中可能叫闭包) 就是大括号,或者 do-end 间圈定的那一部分,可以作为参数传递给方法调用。Ruby 代码块可以将当前上下文锁定并带入方法调用中。一般将代码块放在方法调用的同一行,并作为最后一个参数(当然也可以放在方法调用时,用括号圈定的的参数列表中)。代码块中的代码在初次定义时并不会执行。相反,Ruby 会记得代码块出现时的上下文环境(如本地变量,当前对象等等),并且将其带入调用方法中。Matz 说过,任何方法都可以带一个代码块作为一个隐含参数。在调用方法中,你可以通过 yield 关键字来调用代码块,yield 后也可跟参数。代码块不是对象,但可以被转换成类 Proc 的对象。其中一个方法就是在定义调用方法时,在最后一个参数加上 & 符号做前缀,则此参数回将接受到的 block 作为一个 Proc 对象,如:def my_method(p1, &block)&&...endObject 类中有一个公共方法叫 instance_eval,他可以通过指定对象来调用。它提供了一种能力可以访问该对象的实例变量。调用它时其后可跟 block 或字符串:class Rubyist&&def initialize&&&&@geek = "Matz"&&endendobj = Rubyist.new# instance_eval can access obj通过传入 instance_eval 的 block ,你可以直接操作对象内部。你可以肆意打破封装!没有什么数据可以作为私有。
instance_eval 也可以被用来添加类方法,比如下面演示:class RubyistendRubyist.instance_eval do&&def who&&&&"Geek"&&endendputs Rubyist.who # => GeekDeciding on a simple DSL
假设你是个Ruby 高手,你的朋友 Victor, Michael 和Satoshi (三人都是国际象棋的初学者)请你写个程序,用来计算黑棋的最佳棋路。
你跟朋友说,如果要写这样的程序首先需要他们提供白棋的棋步,提供文本格式如下:
在你写的 DSL 程序中,h4, a3, e4 实际上是 Ruby 方法。只要在 DSL 中提供正确的 Ruby 语法格式,Ruby 会帮我们完成文件解析,并保存数据。
Victor 下了一步黑棋,对手执白走到 h4。Victor 想知道对手为什么这样走,以及为什么第二步又走 a3。
Victor 发了个文件给你:
The DSL program – chess_opener.rb
作为一个 Ruby 专家,你很快写出了第一版的 DSL 程序– :class ChessOpener&&def initialize&&&&@data = {}&&&&load_data&&end&&&&def self.load(filename)&&&&dsl = new&&&&dsl.instance_eval(File.read(filename))&&end&&&&def h4&&&&puts "=========="&&&&puts @data.assoc("h4")&&&&puts "=========="
&&end&&def a3&&&&puts "=========="&&&&puts @data.assoc("a3")&&&&puts "=========="
&&end&&def method_missing(method_name, *args, &block)&&&&msg = "You tried to call the method #{method_name}. There is no such method."&&&&raise msg&&end&&private&&def load_data&&&&@data = {"a3" => ["Anderssen's Opening Polish Gambit: 1. a3 a5 2. b4",&&&&&&&&&&&&&&&&&&&&&&"Anderssen's Opening Creepy Crawly Formation: 1. a3 e5 2. h3 d5",&&&&&&&&&&&&&&&&&&&&&&"Anderssen's Opening Andersspike: 1. a3 g6 2. g4"],&&&&&&&&&&&&&"h4" => ["Koola-Koola continues 1.h4 a5",&&&&&&&&&&&&&&&&&&&&&&"Wulumulu continues 1.h4 e5 2. d4",&&&&&&&&&&&&&&&&&&&&&&"Crab Variation continues 1.h4 any 2. a4",&&&&&&&&&&&&&&&&&&&&&&"Borg Gambit continues 1.h4 g5.",&&&&&&&&&&&&&&&&&&&&&&"Symmetric Variation continues 1.h4 h5"]}&&end&&end代码分析
ChessOpener 类的 initialize& 创建了一个 Hash 对象 @data ,然后通过 private 方法 load_data 加载数据。
这里有写好的
,可以直接用来创建 @data. 现在我们只添加了 a3 和 h4 ,随后我们逐步添加。
你希望能够简单直接的解析 。比如这样:my_dsl = ChessOpener.load(filename)同时,你也希望可以通过命令行接收 DSL 文件,比如这样:my_dsl = ChessOpener.load(ARGV[0])我们可以写一个类方法 load:def self.load(filename)&&dsl = new&&dsl.instance_eval(File.read(filename))end类方法 load 创建一个 ChessOpener 对象,并基于 DSL 文件(就是上面的 chess_opener_test.txt )来调用 instance_eval 。如果你给 instance_eval 传入一个字符串, instance_eval 会把字符串当成 Ruby 代码来执行。 实际上,我们现在这个代码啥都没做,就是调用了一下 h4 和 a3。而 h4 和 a3 不过是调用 Hash 类的 assoc 方法,来提取棋步。
这个程序同时提供了
方法,当某方法不存在时,它会被调用,比如说 h5(比如 Victor 在 chess_opener_test.txt 文件中敲错了某个字符)。
运行 DSL 程序
You next write the program – , 确保这几个文件在同一目录下: chess_opener.rb, chess_opener_test.rb 和chess_opener_test.txt 。
现在你可以运行了:
ruby chess_opener_test.rb chess_opener_test.txt
这里是输出结果:==========h4Koola-Koola continues 1.h4 a5Wulumulu continues 1.h4 e5 2. d4Crab Variation continues 1.h4 any 2. a4Borg Gambit continues 1.h4 g5.Symmetric Variation continues 1.h4 h5====================a3Anderssen's Opening Polish Gambit: 1. a3 a5 2. b4Anderssen's Opening Creepy Crawly Formation: 1. a3 e5 2. h3 d5AnderssenIn fact, in the next version of your DSL program, you plan to write the output to a file and send the same to Victor. Why don’t you
and add-on some more functionality?
That’s it!
Feel free to ask questions and give feedback in the comments section of this post. Fellow Rubyists, if you would like to write a guest blog post for RubyLearning email me at satish [at] rubylearning.org
Technorati Tags: , , ,
Posted by Satish Talim
阅读(1558) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
请登录后评论。Java8采用Martin Fowler的方法创建内部DSL - ImportNew
| 标签: ,
我最近在读Martin Flower写的一本非常棒的关于DSLs()的书。围绕DSLs及语言的内容使得我们可以很方便地创建DSLs,DSLs的使用让我对DSLs的概念更加好奇,这本书让人印象深刻。在Martin Fowler一书的开始是这样定义DSLs的:
Domain-specific language (noun): 一种专注于某一领域,仅针对部分表达方式的计算机编程语言。(译者注:求专不求全。)
DSL不是什么新鲜玩意,很久以前,人们就将XML作为一种DSL的一种形式来使用了。将XML作为DSL来使用非常便捷,因为我们可用来检查DSL的XSD,有解释DSL的解释器,还有能将DSL转换成其他语言的XSLT。并且,多数的语言都对解释XML和获取该语言领域中模型对象的内容提供了很好的支持。像是Ruby,Groovy等等这些语言的出现使得DSL被更广泛的接受。比如Rails,一个使用Ruby写的Web框架,广泛地采用了DSLs。
Martin Fowler在中将DSLs分为三类:内部的DSL,外部的DSL,和语言工作平台的DSL。当我读到内部DSL概念时,使用Java作为宿主语言,用我自己简单的DSL小试牛刀了一下。内部DSLs驻于宿主语言中,并且遵守宿主语言的语法。尽管使用Java作为宿主语言没有让我非常清楚的了解DSL,但却有效地让我以一种合适的方式来了解DSL。
我打算写一个能产生图表的DSL。在Java中有两种接收输入并产生表格的方法:和。然而我发现,在没有将矩阵作为“一等公民”提供支持的语言(尤其是Java)实现是非常困难的,所以,我就尝试着在Java中写一个内部DSL来实现对表格的操作。
在他的书中,Martin Fowler强调需要保持语义模型不同于DSL,并且引入了一个中间表达式构造器来从DSL中产生语义模型。所以为了遵守上述内容,我通过写不同的DSL语法和表达式构造器实现了三种不同的DSLs形式,同时又使用了相同的语义模型。
理解语义模型
在这种情景下,语义模型就是包含了Edge实例数组的Graph类,每一个Edge对象又存放了从Vertex到Vertex的数据和一个weight。下面看一下代码吧。
Graph.java
import java.util.ArrayL
import java.util.L
import java.util.S
import java.util.TreeS
public class Graph {
private List&Edge&
private Set&Vertex&
public Graph() {
edges = new ArrayList&&();
vertices = new TreeSet&&();
public void addEdge(Edge edge){
getEdges().add(edge);
public void addVertice(Vertex v){
getVertices().add(v);
public List&Edge& getEdges() {
public Set&Vertex& getVertices() {
public static void printGraph(Graph g){
System.out.println(&Vertices...&);
for (Vertex v : g.getVertices()) {
System.out.print(v.getLabel() + & &);
System.out.println(&&);
System.out.println(&Edges...&);
for (Edge e : g.getEdges()) {
System.out.println(e);
public class Edge {
private Vertex fromV
private Vertex toV
public Edge() {
public Edge(Vertex fromVertex, Vertex toVertex, Double weight) {
this.fromVertex = fromV
this.toVertex = toV
this.weight =
public String toString() {
return fromVertex.getLabel()+& to &+
toVertex.getLabel()+& with weight &+
getWeight();
public Vertex getFromVertex() {
return fromV
public void setFromVertex(Vertex fromVertex) {
this.fromVertex = fromV
public Vertex getToVertex() {
return toV
public void setToVertex(Vertex toVertex) {
this.toVertex = toV
public Double getWeight() {
public void setWeight(Double weight) {
this.weight =
Vertex.java
public class Vertex implements Comparable&Vertex& {
public Vertex(String label) {
this.label = label.toUpperCase();
public int compareTo(Vertex o) {
return (this.getLabel().compareTo(o.getLabel()));
public String getLabel() {
public void setLabel(String label) {
this.label =
好了,既然语义模型已经到位,我们就开始做DSLs吧。你应该已经注意到了吧,我不打算修改我的语义模型。没有硬性规定语义模型不可以修改,相反,随着新的可以读取和修改数据的API的加入,语义模型可以不断被完善。但是将语义模型和DSL绑定的太死并不可取。保持它们相对分离可以独立地测试语义模型和DSL。
Martin Fowler陈述的创建内部DSLs 的方法是:
Lambda表达式/闭包
除了功能序列之外,我在这篇文章中图文并茂的介绍了其中的3种。但是我也在Lambda表达式/闭包中使用了功能序列的方法。
用方法链创建的DSL
我幻想出一种像这样子的DSL:
.from(&a&)
.weight(12.3)
.from(&b&)
.weight(10.5)
为了能够实现这样的DSL,我们要写一个表达式构造器,能够产生语义模型,提供能产生DSL的。
我写了来年改革表达式构造器——一个用来完成图表,另一个用来建立每一个的边界。这些图表和边界建立的时候,这些表达式构造器就持有中间的图表和边界对象。以上的语法可以在表达式构造器的静态方法中实现,然后用静态导入,就可以在DSL中使用了。
Graph()方法开始生成Graph模型,同时edge()和一系列方法,也就是from(),to(),weight()产生Edge模型,edge()同时也产生Graph模型。
让我们来看一下GraphBuilder(生成Graph模型的表达式构造器)吧:
GraphBuilder.java
public class GraphBuilder {
public GraphBuilder() {
graph = new Graph();
//Start the Graph DSL with this method.
public static GraphBuilder Graph(){
return new GraphBuilder();
//Start the edge building with this method.
public EdgeBuilder edge(){
EdgeBuilder builder = new EdgeBuilder(this);
getGraph().addEdge(builder.edge);
public Graph getGraph() {
public void printGraph(){
Graph.printGraph(graph);
接下来是EdgeBuilder(生成Edge模型的表达式构造器):
EdgeBuilder.java
public class EdgeBuilder {
//Keep a back reference to the Graph Builder.
GraphBuilder gB
public EdgeBuilder(GraphBuilder gBuilder) {
this.gBuilder = gB
edge = new Edge();
public EdgeBuilder from(String lbl){
Vertex v = new Vertex(lbl);
edge.setFromVertex(v);
gBuilder.getGraph().addVertice(v);
public EdgeBuilder to(String lbl){
Vertex v = new Vertex(lbl);
edge.setToVertex(v);
gBuilder.getGraph().addVertice(v);
public GraphBuilder weight(Double d){
edge.setWeight(d);
让我们来试一下这个DSL吧:
public class GraphDslSample {
public static void main(String[] args) {
.from(&a&)
.weight(40.0)
.from(&b&)
.weight(20.0)
.from(&d&)
.weight(50.5)
.printGraph();
.from(&w&)
.weight(23.0)
.from(&d&)
.weight(34.5)
.from(&e&)
.weight(50.5)
.printGraph();
输出结果是:
Vertices...
A to B with weight 40.0
B to C with weight 20.0
D to E with weight 50.5
Vertices...
W to Y with weight 23.0
D to E with weight 34.5
E to Y with weight 50.5
这个方法不是比Adjacency List或者Adjacency Matrix方法更具有可读性吗?这个方法链和我之前写的很像。
用嵌套函数创建的DSL
在DSL中使用嵌套函数的风格会有所不同。在这中方法中,我将会在函数之中嵌套函数,来写我的语义模型,向下面这样:
edge(from(&a&), to(&b&), weight(12.3),
edge(from(&b&), to(&c&), weight(10.5)
这种方法的好处是,它的层次天生juice不像访法链那样必须用另一种格式来写代码。而且,这种方法不需要在表达式构造器中提供中间变量,也就是说,当DSL被解析或者执行的时候,表达式构造器不需要持有Graph和Edge对象。语义模型和上文中谈到的相同。
以下是DSL的表达式构造器。
NestedGraphBuilder.java
//Populates the Graph model.
public class NestedGraphBuilder {
public static Graph Graph(Edge... edges){
Graph g = new Graph();
for(Edge e : edges){
g.addEdge(e);
g.addVertice(e.getFromVertex());
g.addVertice(e.getToVertex());
NestedEdgeBuilder.java
//Populates the Edge model.
public class NestedEdgeBuilder {
public static Edge edge(Vertex from, Vertex to,
Double weight){
return new Edge(from, to, weight);
public static Double weight(Double value){
NestedVertexBuilder.java
//Populates the Vertex model.
public class NestedVertexBuilder {
public static Vertex from(String lbl){
return new Vertex(lbl);
public static Vertex to(String lbl){
return new Vertex(lbl);
如果你想严格遵守规则,让所有表达式构造器定义在静态上,我们可以使用静态导入的方法创建一个DSL。
注意:表达式构造器、语义模型和DSL分别在不同的包中,所以请根据您的包名更新一下import。
//Update this according to the package name of your builder
import static nestedfunction.NestedEdgeBuilder.*;
import static nestedfunction.NestedGraphBuilder.*;
import static nestedfunction.NestedVertexBuilder.*;
* @author msanaull
public class NestedGraphDsl {
public static void main(String[] args) {
Graph.printGraph(
edge(from(&a&), to(&b&), weight(23.4)),
edge(from(&b&), to(&c&), weight(56.7)),
edge(from(&d&), to(&e&), weight(10.4)),
edge(from(&e&), to(&a&), weight(45.9))
输出如下:
Vertices...
A to B with weight 23.4
B to C with weight 56.7
D to E with weight 10.4
E to A with weight 45.9
有趣的部分来了:我们如何利用DSL支持的lambda表达式呢?
在内部DSL中使用lambda表达式
如果你还不知道lambda表达式在Java中能做什么的话,请先阅读本文有关语义模型的部分。
在这个例子中我们会继续使用上面描述的语义模型。这个DSL使用了支持lambda表达式的功能序列。让我们看一下最后的DSL是什么样子的吧:
Graph(g -& {
g.edge( e -& {
e.from(&a&);
e.to(&b&);
e.weight(12.3);
g.edge( e -& {
e.from(&b&);
e.to(&c&);
e.weight(10.5);
是的,我知道上面的这个DSL重载了操作符,但是我们不得不这样做。如果你不喜欢,那么可以选择另一种语言。
在这个方法中,我们的表达式构造器应该接受lambda表达式/closure/block,然后在lambda表达式/closure/block的基础上创建语义模型。这样实现的语义模型保留了Graph和Edge对象这样的中间值,就想我们在方法链中做的那样。
看一下我们的表达式构造器吧:
GraphBuilder.java
//Populates the Graph model.
public class GraphBuilder {
public GraphBuilder() {
g = new Graph();
public static Graph Graph(Consumer&GraphBuilder& gConsumer){
GraphBuilder gBuilder = new GraphBuilder();
gConsumer.accept(gBuilder);
return gBuilder.g;
public void edge(Consumer&EdgeBuilder& eConsumer){
EdgeBuilder eBuilder = new EdgeBuilder();
eConsumer.accept(eBuilder);
Edge e = eBuilder.edge();
g.addEdge(e);
g.addVertice(e.getFromVertex());
g.addVertice(e.getToVertex());
EdgeBuilder.java
//Populates the Graph model.
public class GraphBuilder {
public GraphBuilder() {
g = new Graph();
public static Graph Graph(Consumer&GraphBuilder& gConsumer){
GraphBuilder gBuilder = new GraphBuilder();
gConsumer.accept(gBuilder);
return gBuilder.g;
public void edge(Consumer&EdgeBuilder& eConsumer){
EdgeBuilder eBuilder = new EdgeBuilder();
eConsumer.accept(eBuilder);
Edge e = eBuilder.edge();
g.addEdge(e);
g.addVertice(e.getFromVertex());
g.addVertice(e.getToVertex());
在GraphBuilder中有两行高亮的代码。这个地方用了Java 8引入的,.
下面我们使用上面的表达式构造器来创建我们的DSL。
//Update the package names with the ones you have given
import graph.G
import static builder.GraphBuilder.*;
public class LambdaDslDemo {
public static void main(String[] args) {
Graph g1 = Graph( g -& {
g.edge( e -& {
e.from(&a&);
e.to(&b&);
e.weight(12.4);
g.edge( e -& {
e.from(&c&);
e.to(&d&);
e.weight(13.4);
Graph.printGraph(g1);
输出如下:
Vertices...
A to B with weight 12.4
C to D with weight 13.4
当我要结束这篇长长的文章的时候,我知道你可能想让我把它分成3个部分每部分介绍一种DSL的实现来发布。我坚持发布在一篇文章中是因为这样可以比较一下这3种方法。
这篇文章中我们讨论了Martin Fowler的Domain Specific Languages一书中涉及的DSL,内部DSL。
分别介绍了三种背部DSL的实现方法
支持功能序列的Lambda表达式
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(了解我更多,在:)
第59题,我觉得是有争议的。如果不同的线程拿到的锁对象是一样的,其他线程是无法访问A方法或者是B方法...
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:ImportNew.
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2018 ImportNew宽带及其DSL技术解析_图文_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
宽带及其DSL技术解析
&&宽带及其DSL技术解析
阅读已结束,下载本文需要
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩4页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢新人想问买new 3dsll淘宝哪家好_百度知道
新人想问买new 3dsll淘宝哪家好
我有更好的答案
只要是大商家就行,巴士的贵些,不过服务绝对没二话,其他商家不清楚。以后这种问题最好别问,相信销量就行了。问了也全是打广告的我在巴士买的,其他商家不清楚
采纳率:49%
来自团队:
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。新手求助,声卡驱动里的.dsl文件怎么用。。??
本回答由提问者推荐
var sogou_ad_id=731547;
var sogou_ad_height=160;
var sogou_ad_width=690;

我要回帖

更多关于 买新车有问题怎么投诉 的文章

 

随机推荐