解码Clojure代码,让您不知所措

最近,我写了一些有趣的功能Kotlin来解决FizzBu??zz测试 。 我要求提供一些反馈,我收到的答案之一是Clojure :

左侧的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
( defn   div-by?   [ n   d ]
   ( zero?   ( mod   n   d )))

 ( defn   fizz-buzz   [ n ]
   ( condp   =   [( div-by?   n   3 )   ( div-by?   n   5 )]
          [ true   true ]   "FizzBuzz"
          [ true   false ]   "Fizz"
          [ false   true ]   "Buzz"
          ( str   n )))

 ( ->>   ( range   1   100 )
      ( map   ( comp   println   fizz-buzz )))

我对Clojure一无所知,但这只是一种功能性语言。 我认为尝试理解以上摘录是一个有趣的练习,特别是因为它的大小非常有限。 在本文中,我将采用Java开发人员的观点。

括号,到处都有括号

如果您只熟悉C语言家族(例如我自己),那么这段代码看起来就很晦涩。 首先要知道Clojure属于Lisp语言家族。 后者在每个表达式,语句,调用等周围加上括号:

1
( str   n )   ; returns n.toString()

没有静态类型检查

Clojure没有静态类型检查。 没有。 零。 齐尔奇 您可以在上面的解决方案中检查是否缺少类型。

如果真的对此感到不舒服,则有一个子项目将类型引入Clojure。

没有返回关键字

Clojure是一种功能语言。 因此,它会尽最大努力来强制编写函数:

函数是将集合X的每个元素x(函数的域)与另一个集合Y的单个元素y相关联的过程或关系。

—维基百科
https://zh.wikipedia.org/wiki/功能_(数学)

这意味着每个函数都应该返回。 因此,如上述片段所示, return关键字是隐式的。

不应该返回值的函数-不纯函数( 例如 println返回nil

前缀表示法

Clojure需要先编写方法名称,然后再编写参数(如果需要)。 这与Java非常相似,但是Clojure将该模式应用于所有地方 ,甚至用于算术:

1
( mod   n   d )   ; returns the reminder of n divided by d

编码约定

  • Clojure标识符使用连字符,而不是像Java中那样使用驼峰式大小写
    1
    ( div-by?   n   3 )   ; calls the div-by? function with arguments n and 3
  • 返回boolean值的函数应以?结尾

定义功能

defn宏允许定义一个新函数。 宏的参数为:

  1. 函数名称
  2. 参数列表,指定为可选的空数组
  3. 功能体
1
2
3
4
( defn
   div-by?             <i class="conum" data-value="1"></i> <b class="raw_b_node">(1)</b>
   [ n   d ]               <i class="conum" data-value="2"></i> <b class="raw_b_node">(2)</b>
   ( zero?   ( mod   n   d )))  <i class="conum" data-value="3"></i> <b class="raw_b_node">(3)</b>
1个 功能名称
2 函数参数,包装在数组中
3 功能体

链接功能

函数链接是通过comp宏实现的:

1
2
( def   times-inc   ( comp   inc   * ))  <i class="conum" data-value="1"></i> <b class="raw_b_node">(1)</b>
 ( times-inc   2   3 )               <i class="conum" data-value="2"></i> <b class="raw_b_node">(2)</b>
1个 组成乘和加一的函数
2 用参数23调用定义的函数,返回7

开关

condp宏代替了传统的switch语句,但功能更强大。 它更类似于Kotlin和Scala中的模式匹配功能。

1
2
3
4
5
(condp = [(div-by? n 3) (div-by? n 5)] ; a two-elements array of boolean
  [true true] "FizzBuzz"
  [true false] "Fizz"
  [false true] "Buzz"
  (str n)) ; if no other match, this is the default

摘要

综上所述,以下是上述代码段的Java等效项(或Java不够用时的Kotlin):

Clojure Java /科特琳
1
( str   n )
1
return n . toString ();
1
( mod   n   d )
1
return n % d ;
1
( div-by?   n   3 )
1
return divBy ( n , 3 );
1
2
( defn   div-by?   [ n   d ]
   ( zero?   ( mod   n   d )))
1
2
3
boolean divBy ( int n , int d ) {
  return n % d == 0 ;
}
1
2
3
( def   times-inc
   ( comp   inc   * ))
 ( times-inc   2   3 )
1
2
3
4
5
UnaryOperator < Integer > inc = i -> i + 1 ;
BinaryOperator < Integer > times = ( i , j ) -> i * j ;
BiFunction < Integer , Integer , Integer > timesInc =
  times . andThen ( inc );
return timesInc . apply ( 2 , 3 );
1
2
3
4
5
6
(condp = [(div-by? n 3)
          (div-by? n 5)]
  [true true] "FizzBuzz"
  [true false] "Fizz"
  [false true] "Buzz"
  (str n))
1
2
3
4
5
6
return when ( divBy ( i , 3 ) to divBy ( i , 5 )) {
  true to true -> "FizzBuzz"
  true to false -> "Fizz"
  false to true -> "Buzz"
  else -> i . toString ()
}

去做

我仍然需要进一步深化一些细微之处:

  • 宏和函数之间的区别
  • →>
  • defdefn之间的区别

之后,可能的后续步骤包括:

  1. 解码原始推文右侧显示的代码
  2. 潜入收藏
  3. 了解Clojure / Java互操作性
  4. 尝试开发基本的Spring Boot应用程序
  5. 检查“经典”堆栈的Java框架的等效形式, 例如 Web开发,持久性,日志记录,构建等。

更进一步:

  • Clojure编程语言
  • 通过示例Clojure
  • Clojure-JVM的函数式编程

关注@nicolas_frankel

翻译自: https://blog.frankel.ch/decoding-clojure-code/