2014-09-29 46 views
1

根據下面的條件改變本地的idomatic方法是什麼?這裏我根據某些條件改變x的價值。Clojure根據條件改變綁定本地值

(defn person-story 
[person] 
    (let [x (str "My name is " (:firstname person) " " (:lastname person) ".") 
    x (if (:showaddress person) (str x " I live in " (:address person) ".") x) 
    x (if (:showage person) (str x " I am " (:age person) " yrs old. ") x) 
    x (if (seq (:hobby person)) (str x " I like " (clojure.string/join ", " (:hobby person)) ".") x)] 
x)) 

(person-story {:firstname "John" :lastname "Doe" :age 45 :showage false :address "67 Circe Ave" :showaddress true :hobby ["movie" "music" "money"]}) 

這將輸出:

"My name is John Doe. I live in 67 Circe Ave. I like movie, music, money." 

如果我是這樣做在Java中,我會做這樣的:

StringBuilder sb = new StringBuilder("My name is "); 
    sb.append(person.get("firstname")).append(" "); 
    sb.append(person.get("lastname")).append(" "); 
    if (showaddress) sb.append("I live in ").append(person.get("address")).append(" "); 
    if (showage) sb.append("I am ").append(person.get("age")).append(" yrs old. "); 
    List<String> hobbies = person.get("hobby"); 
    if (hobbies != null && !hobbies.isEmpty()) sb.append("I like "). append(StringUtils.join(hobbies, ", ")); 
    return sb.toString() 

什麼是實現的最佳途徑同樣的事情,我在clojure上面實現了嗎?對不起,我不能提出一個更好的。


解決方案

謝謝XSC和amalloy,無論答案是偉大的。我接受了amalloy的回答,因爲它顯示了一種全新的解決問題的方式,但是兩者都提高了。 這裏有兩個建議的方式解決的片段:

amalloy的方法:

(defn person-story2 [person] 
    (let [tests [[:showaddress #(format " I live in %s." (:address %))] 
       [:showage #(format " I am %s yrs old." (:age %))] 
       [(comp seq :hobbies) #(format " I like %s." (clojure.string/join ", " (:hobbies %)))]]] 
    (apply str (format "My name is %s %s." (:firstname person) (:lastname person)) 
      (for [[test f] tests 
       :when (test person)] 
      (f person))))) 


(person-story2 {:firstname "John" :lastname "Doe" :showage true :age 50 :showaddress true :address "Universal Studios" :hobbies ["movies" "music" "money"]}) 

輸出:

"My name is John Doe. I live in Universal Studios. I am 50 yrs old. I like movies, music, money." 

XSC的方法:

(defn person-story 
    [{:keys [firstname lastname address showaddress age showage hobbies] :as person}] 
    (cond-> 
    (str "My name is " firstname " " lastname ". ") 
    showaddress (str "I live in " address ". ") 
    showage (str "I am " age " yrs old. ") 
    (seq hobbies) (str "I like " (clojure.string/join ", " hobbies)))) 

(person-story {:firstname "John" :lastname "Doe" :showage false :age 50 :address "Universal Studios" :showaddress true :hobbies ["movies" "music" "money"]}) 

輸出:

"My name is John Doe. I live in Universal Studios. I like movies, music, money" 

回答

1

爲了避免重複使用任何無用的內容(例如,如果條件,或者你是通過一切線程穿過的話),定義你想運行的測試列表,以及測試成功時應用的函數。那麼你可以減少這個清單。在通過反覆調用str來構建字符串的簡單情況下,您可以刪除中間商一點,然後通過apply自己調用str:

(defn person-story [person] 
    (let [tests [[:showaddress #(format " I live in %s." (:address %))] 
       [:showage #(format " I am %s yrs old." (:age %))] 
       [(comp seq :hobby) #(format " I like %s." (clojure.string/join ", " (:hobby %)))]]] 
    (apply str (format "My name is %s %s." (:firstname person) (:lastname person)) 
      (for [[test f] tests 
       :when (test person)] 
      (f person))))) 
2

由於Clojure的1.5有一些工作,就像->/->>用條件確定是否進行單一步驟cond->/cond->>

(cond-> 
    (str "My name is " (:firstname p) " " (:lastname p) ".") 
    (:showaddress p) (str "I live in " (:address p) ".") 
    (:showage p)  (str "I am " (:age p) " yrs old.") 
    ...) 

這將是用於條件字符串建築慣用溶液。或者,你可以使用的東西線沿線的:

(clojure.string/join 
    [(str "My name is " (:firstname p) " " (:lastname p) ".") 
    (if (:showaddress p) 
    (str "I live in " (:address p) ".")) 
    ...]) 

它使用的事實,nil將加入字符串時被忽略。