如何将函数应用于 Clojure 中的范围?

How to apply a function to a range in Clojure?

我是 Clojure 的新手,我尝试使用来自 clojuredocs.org 的示例数据。

;; Data
(def scenes [{:subject "Frankie"
              :action "say"
              :object "relax"}

         {:subject "Lucy"
          :action "loves"
          :object "Clojure"}

         {:subject "Rich"
          :action "tries"
          :object "a new conditioner"}])

(defn play [scenes n]
  "Play a scene"
  (->>
   scenes
   (#(get % n))
   ((juxt :subject :action :object))
   (interpose " ")
   (apply str)))

play 函数工作正常:

my-stuff.core> (play scenes 0)
"Frankie say relax"
my-stuff.core> (play scenes 1)
"Lucy loves Clojure"
my-stuff.core> (play scenes 2)
"Rich tries a new conditioner"

play-all 函数不起作用:

(defn play-all [scenes]
  "Play all the scenes"
  (let [x (count scenes)]
    (for [n (range x)]
      (map play scenes n ))))

如何更正此 play-all 函数,即如何将播放函数应用于数据范围?

您不需要for map

只有 for:

user=> (defn play-all [scenes]
  #_=>   "Play all the scenes"
  #_=>   (let [x (count scenes)]
  #_=>     (for [n (range x)]
  #_=>       (play scenes n ))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")

并且只有 map:

user=> (defn play-all [scenes]
  #_=>   "Play all the scenes"
  #_=>   (let [x (count scenes)]
  #_=>     (map #(play scenes %1) (range x))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")

(我更喜欢后者。)

编辑:如果你喜欢->>,这个就更好了:

user=> (defn play-all [scenes]
  #_=>   "Play all the scenes"
  #_=>   (->> scenes
  #_=>     (count)
  #_=>     (range)
  #_=>     (map #(play scenes %))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")

map 迭代一个集合(或多个集合)以产生一个序列。 forlist comprehension 构建一个序列。 在您的情况下,您可以使用其中一个 另一个。

在分解方面,实际上有一个播放一个场景的函数是有意义的:

(defn play-one [scene]
  "Play a scene"
  (->>
   scene
   ((juxt :subject :action :object))
   (interpose " ")
   (apply str)))

那么打第n可以用先例定义:

(defn play-nth [scenes n]
  "Play the n(th) scene"
  (->
   scenes
   (#(get % n))
   play-one))

您有多种方式来播放所有场景:

(defn play-all-map1 [scenes]
  "Play all the scenes"
 (map (partial play-nth scenes) (range (count scenes))))

但是你真的可以简化,因为你不需要 range 因为 scenes 可以被视为一个序列(假设你对索引不感兴趣):

(defn play-all-map2 [scenes]
  "Play all the scenes with map"
  (map play-one scenes))

for:

(defn play-all-for [scenes]
  "Play all the scenes with for"
  (for [scene scenes]
    (play-one scene)))

在我们对其进行任何操作之前,您的 play 函数应该处理单个场景:

(defn play [scene]
  "Play a scene"
  (->> scene
   ((juxt :subject :action :object))
   (interpose " ")
   (apply str)))

你这样使用它:

(play (scenes 0))
;"Frankie say relax"

...这并不比以前更容易或更难。但是

  • 它适用于任何场景并且
  • 场景不必保存在向量中。

它也使 play-all 更简单:

(defn play-all [ss]
  (map play ss))

(play-all scenes)
;("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")

我很想在 play 中用 vals 替换 ((juxt :subject :action :object)),但我们不能依赖映射条目的顺序。