如何使用可变参数进行过滤?
How to do a filter with varargs?
我有一张用户地图和他们最喜欢的乐队:
(def data
{
:David {"Tribalistas" 3.0
"Daft Punk" 5.0
"Lorde" 4.0
"Fall Out Boy" 1.0}
:Matt {"Imagine Dragons" 3.0
"Daft Punk" 4.0
"Lorde" 4.0
"Fall Out Boy" 1.0}
:Ben {"Kacey Musgraves" 4.0
"Imagine Dragons" 3.0
"Lorde" 3.0
"Fall Out Boy" 1.0}
}
)
并且我需要过滤具有两个共同键的结果,在本例中为 band1 和 band2
(defn common-ratings [band1 band2 ratings]
(filter #(and ((second %) band1) ((second %) band2)) ratings))
(common-ratings "Daft Punk" "Lorde" data) ; should return David and Matt lines
但是现在,我需要在可变参数中转换波段,我尝试使用类似的东西:
apply and...
所以我可以像这样使用函数:
(common-ratings "Daft Punk" "Lorde" "Another band" "Another Band2" data)
但它不起作用。
提前致谢
其中一个选项是将 and
宏包装到函数中:
(defn and-fn [x y] (and x y))
然后你可以使用reduce
代替apply
来达到相同的结果:
(reduce and-fn [true true true false]) ; => false
为方便起见,可以将最后一个 reduce 包装到另一个函数中:
(defn and-multi [& args]
(reduce #(and %1 %2) true args)) ; initial value is needed for invocation with no args
现在它的行为与普通 and
宏非常相似:
(and-multi) ; => true
(and-multi false true) ; => false
(apply and-multi [true true true]) ; => true
它甚至会与 args 的评估具有相同的行为:
(and false (range)) ; => false (despite of second param being infinite sequence)
(and-multi false (range)) ; => false
- 如前所述,
and
是一个宏,因此不能作为参数
函数,apply
或任何其他函数。
- 最接近
and
的标准函数是 every?
。
由于您的波段数量未知,请将它们作为集合传递:
(defn common-ratings [bands ratings]
(filter #(every? (val %) bands) ratings))
... 我将 second
替换为 val
以表明我们正在处理地图条目。
例如,
(common-ratings ["Daft Punk" "Lorde"] data)
;([:David {"Tribalistas" 3.0, "Daft Punk" 5.0, "Lorde" 4.0, "Fall Out Boy" 1.0}] [:Matt {"Imagine Dragons" 3.0, "Daft Punk" 4.0, "Lorde" 4.0, "Fall Out Boy" 1.0}])
如果您想将 bands
作为单独的参数传递,请将它们放在最后以将它们捕获为 rest 参数:
(defn common-ratings [ratings & bands]
... )
...你这样称呼:
(common-ratings data "Daft Punk" "Lorde")
...效果同上。
非常感谢你们,我的最终解决方案非常接近@Thumbnail 解决方案:
(defn common-ratings [& bands] (filter #(every? (second %) bands) data))
我有一张用户地图和他们最喜欢的乐队:
(def data
{
:David {"Tribalistas" 3.0
"Daft Punk" 5.0
"Lorde" 4.0
"Fall Out Boy" 1.0}
:Matt {"Imagine Dragons" 3.0
"Daft Punk" 4.0
"Lorde" 4.0
"Fall Out Boy" 1.0}
:Ben {"Kacey Musgraves" 4.0
"Imagine Dragons" 3.0
"Lorde" 3.0
"Fall Out Boy" 1.0}
} )
并且我需要过滤具有两个共同键的结果,在本例中为 band1 和 band2
(defn common-ratings [band1 band2 ratings]
(filter #(and ((second %) band1) ((second %) band2)) ratings))
(common-ratings "Daft Punk" "Lorde" data) ; should return David and Matt lines
但是现在,我需要在可变参数中转换波段,我尝试使用类似的东西:
apply and...
所以我可以像这样使用函数:
(common-ratings "Daft Punk" "Lorde" "Another band" "Another Band2" data)
但它不起作用。
提前致谢
其中一个选项是将 and
宏包装到函数中:
(defn and-fn [x y] (and x y))
然后你可以使用reduce
代替apply
来达到相同的结果:
(reduce and-fn [true true true false]) ; => false
为方便起见,可以将最后一个 reduce 包装到另一个函数中:
(defn and-multi [& args]
(reduce #(and %1 %2) true args)) ; initial value is needed for invocation with no args
现在它的行为与普通 and
宏非常相似:
(and-multi) ; => true
(and-multi false true) ; => false
(apply and-multi [true true true]) ; => true
它甚至会与 args 的评估具有相同的行为:
(and false (range)) ; => false (despite of second param being infinite sequence)
(and-multi false (range)) ; => false
- 如前所述,
and
是一个宏,因此不能作为参数 函数,apply
或任何其他函数。 - 最接近
and
的标准函数是every?
。
由于您的波段数量未知,请将它们作为集合传递:
(defn common-ratings [bands ratings]
(filter #(every? (val %) bands) ratings))
... 我将 second
替换为 val
以表明我们正在处理地图条目。
例如,
(common-ratings ["Daft Punk" "Lorde"] data)
;([:David {"Tribalistas" 3.0, "Daft Punk" 5.0, "Lorde" 4.0, "Fall Out Boy" 1.0}] [:Matt {"Imagine Dragons" 3.0, "Daft Punk" 4.0, "Lorde" 4.0, "Fall Out Boy" 1.0}])
如果您想将 bands
作为单独的参数传递,请将它们放在最后以将它们捕获为 rest 参数:
(defn common-ratings [ratings & bands]
... )
...你这样称呼:
(common-ratings data "Daft Punk" "Lorde")
...效果同上。
非常感谢你们,我的最终解决方案非常接近@Thumbnail 解决方案:
(defn common-ratings [& bands] (filter #(every? (second %) bands) data))