2011-09-15 150 views
2

我的解決方案工作this koan中定義的方法,其中規定:使用另一種方法

Write a function which replicates each element of a sequence a variable number of times. 

要做到這一點,我想:

  1. 做這需要一個序列的方法以及重複每個元素的次數。
  2. 在此方法中定義一個本地方法,將一個值v,n重複成一個序列。

考慮到這一點,我寫了下面的方法:

(fn dupSeqX [aseq x] 
    (fn dupx [v x] 
    (if (= x 1) (list v) (concat v (dupx v (- x 1)))) 
) 
    (reverse (reduce #(concat %1 (dupx %2 x)) '() aseq))) 

運行此代碼,我得到以下錯誤:

java.security.PrivilegedActionException: java.lang.Exception: Unable to resolve symbol: dupx in this context (NO_SOURCE_FILE:0) 

如何去創建一個本地這個方法能讓我完成這個公文嗎?

有沒有一種「clojure-esque」的方式來做到這一點,我不知道?

+2

這裏有一些提示。我認爲你的意思是功能而不是方法。另外,函數是用defn定義的,但匿名函數(lamdba)是用fn定義的。你混淆了兩者。另外,如果您需要在外部函數範圍內使用本地函數,請使用let或letfn。 –

回答

8

首先,我們不談論方法的簡寫:我們談論的功能。 clojure中有些東西可以調用方法,但它與函數不同。如果你停止使用OO術語,你也將失去OO風格。

你正在嘗試做什麼是可能的。您基本上想要在dupseqx函數中創建名稱爲dupx的新函數。你現在正在做的是創建一個函數,然後把它扔掉(你不用做返回值,只返回函數中的最後一個表單)。由於函數與任何其他值一樣,因此可以像使用其他任何值一樣使用相同的機制:創建本地「變量」。這是什麼機制?這是本地綁定和它的工作原理是這樣的(在FN的名稱只是讓你可以從自身調用它,它並不需要是相同let結合的名稱):

(let [dupx (fn dupx [v x] 
      (if (= x 1) 
        (list v) 
        (cons v (dupx v (dec x)))))] 
    (dupx 5 3)) 

公告我糾正了其他一些事情。

這方面的一個較短的形式(固定雙名醜):

(letfn [(dupx [v x] (if (= x 1) 
         (list v) 
         (cons v (dupx v (dec x)))))] 
    (dupx 5 3)) 

好兩者之間「的一切(讓[...]」和匹配的‘)’我們現在有一個dupx功能

所以,現在你的代碼的其餘部分作品:

(fn dupSeqX [aseq x] 
    (letfn [(dupx [v x] (if (= x 1) (list v) (cons v (dupx v (dec x)))))] 
    (reverse (reduce #(concat %1 (dupx %2 x)) '() aseq)))) 

該代碼可以由多一點地道:

  • 編碼指南:名稱參數的coll代替aseq
  • 編碼指南:DoNotUseCamalCase做它喜歡 - 這
  • 遞歸當你不需要它時,對性能和大數據都是不利的。
  • 你正在重新發明車輪。這對學習編碼很有幫助,但如果你想了解語言和標準庫,那就不好了。

我怎麼去寫呢?

首先是基本的fn。 coll是預期序列命名函數的標準。

(fn [coll times] ) 

如果你讀到這個「序列的每個元素」,你的大腦必須去MAP。

(fn [coll times] 
    (map (fn ....) coll)) 

「複製每個...」基本上是你必須放入map函數的描述。我們可以使用repeat(你的配音功能,但有一些額外的好東西,比如懶惰)。

(fn [coll times] 
    (map (fn [val] (repeat times val)) coll)) 

還有一個問題(來自koan)。它希望返回一個seq,而不是每個元素的序列中的一個序列。這意味着我們必須將結果連接在一起。

(fn [coll times] 
    (apply concat (map (fn [val] (repeat times val)) coll))) 

您會經常看到(apply concat (map ....))模式。標準庫中有一個更好的函數,名爲mapcat,我將把內部函數變成簡短的語法。

(fn [coll times] 
    (mapcat #(repeat times %) coll)) 

希望有幫助!

+0

這是一個非常棒的描述,謝謝!我感謝你的洞察力。 – Davidann

2

Clojure有很多或者很大的功能來幫助你進入「思考問題」的習慣。當你發現自己寫的東西在列表中迭代時,會想到「我可以映射這個嗎?」,並且當你發現自己在做任何其他事情時,請查看這個list of useful seq functions

在這種情況下,內部函數恰好是建立在所以你幸運:)經常你需要寫它你的自我,並將其存儲在let雖然如果你使它成爲一個適當的功能,那麼你可能會找到用於它在你的代碼的其他地方。

繼承人提示上手

(flatten (map #(repeat 3 %) [1 2 3 4])) 
(1 1 1 2 2 2 3 3 3 4 4 4) 

PS:#(repeat 3 %)(fn [n] (repeat 3 n))

+0

'(flatten(map ...))' - >'(mapcat ...)'(如果某些項目易於變平?)。恭喜10k。 :-) –

+0

是的,(地圖mapcat亞瑟)。 :)扁平化地圖是一個壞習慣的東西 –