如何将此代码和我的思维转换为功能性思维(Clojure)?

时间:2013-12-05 14:37:48

标签: javascript clojure functional-programming

如何将此JavaScript代码转换为Clojure?

我试图绘制一个(x,y)世界,根据填充属性打开或关闭单元格。在下面的示例中,我尝试打印行然后列,但我的下一步是移动填充属性(向上,向下,向左,向右)。所以,如果我不打印数据结构,我不想要一个不起作用的答案。

我的目标是了解如何以功能的方式思考这个问题。我很容易在JavaScript中用可变状态解决这个问题。在研究解决方案时,我能够轻松地推断出如何在JavaScript中编写代码,但是当我在Clojure中做同样的事情时,我感到很茫然。所以,我觉得这个问题的一个好答案就是帮助我理解如何以功能的方式思考这类问题。

我观看了很多讨论并阅读了一些关于Clojure和函数式编程的文章,但是当它编写代码时,我很难开始。

var world = [
    [{ x: 1, y: 4, fill:0 }, { x: 2, y: 4, fill:0 }, { x: 3, y: 4, fill:0 }, { x: 4, y: 4, fill:0 }],
    [{ x: 1, y: 3, fill:0 }, { x: 2, y: 3, fill:0 }, { x: 3, y: 3, fill:0 }, { x: 4, y: 3, fill:0 }],
    [{ x: 1, y: 2, fill:0 }, { x: 2, y: 2, fill:0 }, { x: 3, y: 2, fill:0 }, { x: 4, y: 2, fill:0 }],
    [{ x: 1, y: 1, fill:0 }, { x: 2, y: 1, fill:0 }, { x: 3, y: 1, fill:1 }, { x: 4, y: 1, fill:0 }]
];

function printworld(world) {
    var out = "";
    for(var row=0; row<world.length; row++) {
        for(var col=0; col<world[row].length; col++) {
            out += "["+world[row][col].fill+"]";
        }
        out += "\n"
    }
    console.log(out);
}

printworld(world);

输出如下:

[0][0][0][0]
[0][0][0][0]
[0][0][0][0]
[0][0][1][0]
编辑:在4clojure.com上花了一些时间解决问题之后,我意识到我试图找出一个比我准备解决的更大问题(例如Clojure中的国际象棋游戏)。在4clojure.com上创建最基本的功能一直很困难,但它正在建立一个关于如何使用功能方法来解决方案的稳定工作知识。

4 个答案:

答案 0 :(得分:3)

如何以功能性方式开始思考?没有捷径和快速答案。您必须花费大量时间尝试以功能方式进行编码。

您可以从变形简单算法开始。但重要的是要记住,在函数式编程中,您关心数据流,而不是如何指示CPU执行此操作。

了解您所用语言的核心功能非常重要。从这些核心功能中,您可以开始转换数据。如果你想要的那种Lego或DNA条纹。

如果您对Clojure特别感兴趣,那么我建议您在4Clojure和Land of Lisp上度过美好时光。

Clojure IRC是向Rockstars Clojure开发人员学习的好地方。友好的社区,肯定有帮助。

永远记住:“默认情况下OOP并不容易,默认情况下FP并不难”。

答案 1 :(得分:1)

数据看起来或多或少相同:

(def world [[{:x 1, :y 2, :fill 0}, {:x 2, :y 2, :fill 0}]
            [{:x 1, :y 1, :fill 0}, {:x 2, :y 2, :fill 1}]])

但是对于打印功能,您可以使用doseq

(defn print-world [world]
  (doseq [row world]
    (doseq [cell row]
      (print (str "[" (:fill cell) "]")))
    (println)))

(print-world world)

;; outputs
;; [0][0]
;; [0][1]

并更改元素,assoc-inupdate-in

; move the filled cell 'up'
(print-world
  (-> world
    (assoc-in [1 1 :fill] 0)     ; set bottom-right fill to 0
    (assoc-in [0 1 :fill] 1)))   ; set top-right fill to 1

;; outputs
;; [0][1]
;; [0][0]

Clojure不适合这种编程,但它是有用的东西。

编辑:至于以功能方式思考,这不是那种可以通过stackoverflow答案轻松传达的技巧。它需要大量的代码编写和阅读其他人的代码。一个在线开始的好地方是Clojure for the Brave and True。让我思考功能的是精彩的Learn you a Haskell for Great Good!

答案 2 :(得分:0)

当试图进入“功能性思维方式”时,有时会看到代码并想办法分离可分离的部分。在此示例中,有三个逻辑阶段:

  • 将数据更改为仅包含过滤器字段
  • 以矢量形式(使用[] s)
  • 打印得很好

如果我们将它们分成他们自己的Clojure表达式,那么也许你可以在其他地方重用其中一个,或者编写更好的测试等,而不是在同一个矩阵中执行这两个动作(不可否认,更有效率)

user> (def world [[{:x 1, :y 4, :fill 0 }, {:x 2, :y 4, :fill 0 }, {:x 3, :y 4, :fill 0 }, {:x 4, :y 4, :fill 0 }],
                  [{:x 1, :y 3, :fill 0 }, {:x 2, :y 3, :fill 0 }, {:x 3, :y 3, :fill 0 }, {:x 4, :y 3, :fill 0 }],
                  [{:x 1, :y 2, :fill 0 }, {:x 2, :y 2, :fill 0 }, {:x 3, :y 2, :fill 0 }, {:x 4, :y 2, :fill 0 }],
                  [{:x 1, :y 1, :fill 0 }, {:x 2, :y 1, :fill 0 }, {:x 3, :y 1, :fill 1 }, {:x 4, :y 1, :fill 0 }]])
#'user/world

user> (defn filter-fill [world] (map #(map :fill %) world))
#'user/filter-fill

user> (filter-fill world)                                                            
((0 0 0 0) (0 0 0 0) (0 0 0 0) (0 0 1 0))

user> (defn vector-world [world]  (mapv #(mapv vector %) world))
#'user/vector-world 

user> (clojure.pprint/pprint (vector-world (filter-fill world)))
[[[0] [0] [0] [0]]
 [[0] [0] [0] [0]]
 [[0] [0] [0] [0]]
 [[0] [0] [1] [0]]]
nil

答案 3 :(得分:0)

这个草图生成世界的状态,而不是改变内存中的结构。在Rich Hickey中,它会生成下一个值,而不是在同一个旧位置存储不同的东西。

// world is an object with the world's state
var world = function(tl, tr, bl, br){
    this.state = {topLeft: tl, topRight: tr, bottomLeft: bl, bottomRight: br};
};

// screen is an object with internal state that represents
// the state of the of the world
var screen = function(aWorld){
    this.state = [[{fill: aWorld.state.topLeft},
           {fill: aWorld.state.topRight}],
          [{fill: aWorld.state.bottomLeft},
           {fill: aWorld.state.bottomRight}]];
};

// showScreen is a function in which all side
// effects occur.
function showScreen(aScreen){
    var out = "";
    for (var line = 0; line < aScreen.state.length; line++){
    for (var pix = 0; pix < aScreen.state[line].length; pix++){
        out += "[" + aScreen.state[line][pix].fill + "]";
    }
    out += "\n"
    }
    console.log(out);
}

// here is the heart of functional style programming:
// generating the next value instead of changing
// some existing state
function nextWorld(tr,tl,br,bl){
    showScreen(new screen(new world(tr,tl,br,bl)))
}

// turn off screen
function screenOff(){
    nextWorld(0,0,0,0)
}

// make a forward slash
function forwardSlash(){
    nextWorld(1,0,0,1)
}