对于大多数的脚本编程语言来说, 提供有现成的分别进入控制台与执行脚本文件的命令. 例如 Scala, Python 默认进入控制台(REPL), 接文件路径为参数则执行脚本文件. 还有分别进入控制台和执行脚本的命令是: irb 与 ruby, groovsh 与 groovy, php -a 与 php, perl -de1 和 perl. 可以 Clojure 本身就没有 clojure 这样的命令. 当我们试图在 Mac 下用 brew install clojure 安装时, 得到的提示是没有 clojure, 应该用brew install leiningen去安装 leiningen, 它是一个类似于 Scala sbt 的工具.
所以启动 Clojure REPL 的命令就是lein repl, 其实还有一个办法来启动 Clojure 的控制台, 因为 Clojure 也是构筑于 JVM 之上的, 所以也能像启 Groovy/Scala 一样通过java指令加载 jar 文件来启动. 去官网 http://clojure.org/ 下载 Clojure 安装包(例如: ), 解压, 假定它的 jar 文件是~/Developers/clojure-1.8.0/clojure-1.8.0.jar, 那么也可以用命令java -jar ~/Developers/clojure-1.8.0/clojure-1.8.0.jar进到 Clojure 控制台.
进到 Clojure 的提示符user=>下就可以测试 Clojure 代码了, 那么如何加载一个写在clj文件里的代码呢? 我们可以在 Clojure 控制台下用方法load-file. 假定 ~/hello.clj 文件的内容是 Read More代码写多了, 总希望能从繁琐的代码中挣脱出来, 编程语言的设计应为快速解决问题为目的. 不断重复的表达式应有相应的语法糖加以简化.
函数式语言设计之初不被人重视, 进入到多核化之后我也渐渐从面向对象转入到了函数式的阵营.
学习设计模式时 Java Swing 堪称各种模式的典范, 但是设计的后用起来顺手又能如何, 不过 Java Swing 的性能更是硬伤, 所以有了 SWT 那种更接近于本地化的组件库.
编程语言有两种哲学: 条条大路通罗马 与 一条大路罗马. 前者可以让语言更灵活, 但却容易走上一条不归路, 譬如 Scala; 后者让你专注于解决实际的问题, 像 Python 的语法非常简单.
填满了博客中文章概要显示中的内容, 可以说一说我希望中的语言的几个特征, 主要关注语法: Read More
- Java 在定义字符串的时候不支持字符串插值, 即不能在字符串中捕获作用域中的变量, 用来组成当前字符串. 而这可以说是其他各种言都具备的基本特性. 例如 Bash 中可以这样
bash-3.2$ name=Yanbin
Java 中不支持类似的方式
bash-3.2$ echo "Hello $name"
Hello YanbinString name = "Yanbin";
而使用 Java 必须用 String 的静态方法 format (注意是静态方法哦) 来间接的格式化出一个字符串, 可以这么写
String greeting = "Hello $name"; //Java 中无法把 $name 替换为 name 变量值 "Yanbin"String.format("Hello %s", "World");
我们看到在用 format 格式化时匹配参数会是一个不小的问题, 相同使用法的打印输出方法是
String.format("%1$s %1$s %s %s %2$s", "aa", 10); //aa aa aa 10 10System.out.printf(). 别说字符串插值了, 如果 format 方法是 String 的一个实例方法应用起来都会便利些, 如"Hello %s".format("World")会容易读些, Scala 的字符串实例就有format方法了. Read More - 希望 Java 能支持动态对象(匿名对象) 的特性是源于想要 Java 方法能优雅的返回多个值. 目前如果希望 Java 方法返回多个值的做法有返回一个自定义对象, 数组或列表, 或 Map. 这种需求多发生在私有方法上, 但目前的解决办法有如下弊端:
- 如果用自定义类来作为返回类型的话, 会使得类过于杂乱, 而且这些自定义类的复用性不高
- 数组或列表有太强的顺序依赖, 没有属性名告知每个位置上值的意义, 而且类型都必须为 Object
- 返回 Map 的, 在维护 Key 上容易出错(或须为 Key 定义很多字符串常量), 且类型也是为 Object.
说了上面许多, 各位可能还不定清楚我想要的是什么, 其实就是类似于 C# 的匿名对象(或曰动态对象), 看一段 C# 的方法返回一个匿名对象的例子(.NET3.5 开始引入匿名对象, .NET 4.0 后匿名对象可以作为函数值) Read More - 曾几何时在业务分层结构中的 VO 或 DTO 层充斥着无数的标准 JavaBean 类, 那些碍手脚的 getter/setter 方法简值不忍直视. 或许 JavaBean 设定规范的用意是当某些属性为只读时不提供 setter 方法, 而实际使用时, 因 getter/setter 都同时具备, 那么 JavaBean 的所有私有属性又何异于公有属性呢.
更别说对于某些形式的属性名, 若属性名为xCoordinate时, 它所对应的 getter 方法分别是getxCoordinate(),一般的 IDE 都会为它自动生成getXCoordinate()方法, 这是错误的. 实际上getXCoordinate()对应的属性名是XCoordinate.
所以 Play Framework 1 以及 Play Framework 2.4.6 之前的版本采用了字节码增强的技术, 实现了像 Objective-C 的 @property 的特性, 即只要声明公有属性, 编译器为该属性生成默认的 getter/setter 方法, 您也可以手工去覆盖个别默认的 getter/setter 方法.
因此在 Play Framework 中书写的的 model 类就只需要属性了, 像1public class User { 2 public int id; 3 public String name; 4 public String email; 5 public String address; 6}
就这么简单, 想像一下如果我们为一个众多属性的 model 类补全所有的 getter/setter 方法读起来有多恐怖. Read More 虽说 Java 的支持泛型以及近期 Lambda 表达式的加入, 在对类型进行推断上已经很强大了, 但在类型声明的时候仍然略显冗余, 最主要的一点是 Java 不能像 Scala 那样在声明变量有赋值的情况下进行类型推断. 我们先来看下 Java 已经为我们所进行的改进:
1List<String> strings = new ArrayList<String>(); //刚引入泛型时我们是这样 2List<String> strings = new ArrayList<>(); //后来变成这样了, 可以钻石符号推断, Java 7 3 4List<String> list = new ArrayList<>(); 5list.addAll(new ArrayList<>()); //根据 list.addAll() 上下文推断要创建的类型是 new ArrayList<String>(), Java 8 6 7interface Foo { 8 void dodo(); 9} 10Foo foo = () -> System.out.println("Foo:dodo()"); //Java 8 Lambda 可以根据 Lambda 表达式的签名推断出接口类型 11foo.dodo();在 Java 7 的泛型方法其实也是可以通过声明类型与方法参数来推断要返回的具体类型的. Read More
- 对于面向对象的语言不知道除了 Java 还有没别的语言会拿怎么比较两个字符串相等频频作为面试题来考. 原本是在编程语言中两个字符串内容是否相等时用
==比较时却可能是不对的. 在 Java 中1"ab" == "ab" //true 2"ab" == "new String("ab") //false 3"ab" == String.value("ab") //true 4new String("ab").equals(new String("ab")) //true 5new String("ab").intern() == new String("ab").intern() //true
在 Java 中明明看到两个字符串内容一样用==进行比较多数时候不是你想要的结果, 只有用equals()方法才是王道. 使用 Java 的字符串必须了解它内部是怎么存储的. 比于上面的结果我不作细说, 主要涉及到字符串常量池及内部状态,==比较引用,equals()比较内容.
Java 还常常对equals比较字符串津津乐道, 而我仍然认为它是语言设计上的一个缺陷, 所以 JVM 上的其他编程语言如 Groovy, Scala 纷纷倒勾, 无一不是用==来比较字符串的内容, 它们也提供字符串引用的比较, 但多少人实际关心两个字符串的引用是否相同呢, 反正字符串设计的是 Immutable 的.
若说是因为 Java 不支持操作符的重载, 但可以像 Scala, Groovy 那样在编译器上下功夫的. 最终我想依然是受累于 100% 源代码与二进制的兼容性, 改进的话会造成早先代码的行为错乱. Read More - Java 语言由于一直要保持源代码及二进制的向后兼容, 所以尽管语法上有了很大的演进, 但有些东西仍然无法触及. 不像有些新生代的语言设计时可以博采众长, 或者像 Scala 那种语言向后兼容性的要求没这么苛刻, Scala 只要保持主, 次版本相同时的兼容性. Java 则不同, 1.0 的代码或字节码放到 1.8 下还要能跑.
实际应用中在多个语言切换时, 很自然的会对其他语言与 Java 进行横向对比. 感觉 Java 语言有几个缺陷, 不在此篇中一一列举, 只把第一个不足之处道来.
Java 没有 Here Document 的支持, Here Document 又称 heredoc, hereis, here-string, here-script, 再通俗点讲就是多行字符串(multiline string). 在 Java 中如果要定义多行字符串, 需要连串的加号及换行符(\n), 如下1String content = "This is a\n" 2 + "multiline\n" 3 + "string content"
曾经为了寻求 Java 对 Here Document 的支持, 尝试过 Java 的 APT(Java Annotation Processing Tool), 定义注解 @HereDocument 使用注释, 编译时带-processor参数, 但是注释中的格式很容易被 IDE 给自动格式化掉, 见 Java 的多行字符串 Here Document 的实现. 所以这种尝试很快就放弃了, 因为项目中用 Java 和 Scala 混合编程, 真正需要 Here Document 的地方直接用 Scala 代码就行了. Read More
Clojure 确实要比 Python 语义上庞大, 所以无法尽量在一篇之中收纳进来, 只得另立新篇, 也许还会有第三篇笔记. 现在开始学习函数定义函数定义
函数用defn宏来定义, 函数名与参数列表之间可选的字符串是函数的注释, 相当于 Python 的函数体中第一个字符串, 这个字符串能被(doc func-name)列出来, Python 中是用dir(fun_name显示函数帮助. 不需要 return 关键字, 和 Groovy/Scala 一样最后一个表达式的值为函数的返回值, 所以函数总是有返回值(或为 nil).
和 C 语言一样, 函数必须先定义再使用, 否则要用(declare function-names)提前声明, 下面代码是在 Clojure 的 REPL 中执行的1user=> (defn say-hello-to 2 "say hello to some" 3 [name] (str "Hello, " name)) 4#'user/say-hello-to 5user=> (doc say-hello-to) 6------------------------- 7user/say-hello-to 8([name]) 9 say hello to some 10nil 11user=>
提一下 Clojure 的命名规则, 变量和函数名用中划线连接的小写单词.defn-定义的函数是私有, 只对当前名字空间可见, 比如上面的 user 名字空间. 有点像 Python 的下划线变量或函数的可见性约定. Read More
自己所学过的编程语言基本是 C 风格的, 给自己定下的目标是要学习下 Python, Swift 和 Clojure. 正如之前的 我的 Python 快速入门 那样的几分钟入门, 这里记录下 Clojure 的快速上手过程.
为什么是 Clojure, 因为它是 Lisp 的一个方言, 个人觉得有必要拓展一下不同的语言风格与思维方式, 就像当初接触 Objective-C 的 [person sayHello] 的方法调用有点不好理解一样, 其实把它还原为面向对象的本质是向 person 发送 sayHello 消息就简单了.编程语方不仅仅是一种技术, 它更是一种思维习惯
希望通过 Clojure 这样的语言来感受另样的思维方式. Clojure 是运行在 JVM 之上的函数式 List 方言. Clojure 乍一看, 基本就是一个括号语言, 它的语法更能体现操作/函数为中心. Clojure 的圆括号兼具 C 风格语言的圆括号(参数列表), 分号(分隔语句), 以及大括号(限定作用域) 的功能. (1 + 2 + 3 +4) 只用写成 (+ 1 2 3 4)。
因为 Clojure 是构筑在 JVM 之上, 所以可以从 http://clojure.org/community/downloads 下载 clojure 的 jar 包, 然后java -jar clojure-1.8.0.jar
就能进到 Clojure 的 REPL(read-eval-print-loop) 控制台了, 就可以开始体验 Clojure 的代码user=> (+ 1 2 3),如果要运行一个已经写好的 Clojure 文件, 如 hello.clj, 就要用java -jar clojure-1.8.0.jar hello.clj来执行. 为方便可以建立一个脚本clj, 内容为java -jar /path/clojure.jar $1
Read More