如何在Clojure中使用reduce实现map

时间:2016-05-13 14:37:04

标签: clojure map-function

在涵盖Clojure for the Brave and True的部分末尾的reduce一书中,有一个挑战:

  

如果你想要一个能让你的头发恢复正常的运动,请尝试使用map

实施reduce

事实证明,这比我想象的要困难得多(至少对我来说是一个Clojure初学者)。几个小时后,我想出了这个:

(defn map-as-reduce
  [f coll]
  (reduce #(cons (f %2) %1) '() (reverse coll)))

这是一个更好的方法吗?我特别感到沮丧的是,我必须反转输入集合才能使其正常工作。它似乎有点不雅观!

6 个答案:

答案 0 :(得分:9)

请记住,您可以在向量的末尾高效插入:

(defn map' [f coll]
  (reduce #(conj %1 (f %2)) [] coll))

示例:

(map' inc [1 2 3])
;=> [2 3 4]

map'与原始map之间的一个区别是,原始map会返回ISeq而不只是Seqable

(seq? (map inc [1 2 3]))
;=> true

(seq? (map' inc [1 2 3]))
;=> false

您可以通过使用map'撰写seq的上述实现来解决此问题:

(defn map' [f coll]
  (seq (reduce #(conj %1 (f %2)) [] coll)))

现在最重要的区别是,虽然原来的map很懒,但这个map'非常渴望,因为reduce非常渴望。

答案 1 :(得分:9)

只是为了好玩: map确实接受多个集合作为参数。这是一个扩展实现:

Sub Fetch_Heures2()

Dim Wbname As String
Dim Wb As Workbook
Dim ws As Worksheet
Dim ws1 As Worksheet
Dim lngCalc As Long
Dim l

ngrow As Long
    Dim lngcol As Long

'Cell C4 (date) Cell B7 (name of employee) Cell I50 (hours on site) Cell H50 (hours in office)
With Application
    .ScreenUpdating = False
    .EnableEvents = False
    lngCalc = .CalculationState
    .Calculation = xlCalculationManual
End With




Set ws1 = ThisWorkbook.Sheets("TempData")
    'change folder path here
    FolderName = "J:\15-0023_Vauquelin\8.0 Phase-construction\FdT fictives"
    Wbname = Dir(FolderName & "\" & "*.xls*")

    'ThisWorkbook.Sheets(1).UsedRange.ClearContents
    Do While Len(Wbname) > 0
        Set Wb = Workbooks.Open(FolderName & "\" & Wbname)
        Set ws = Nothing
        On Error Resume Next
        'change sheet name here
        Set ws = Wb.Sheets("Heures")
        On Error GoTo 0
        If Not ws Is Nothing Then
        lngcol = 0
        lngrow = lngrow + 1
        lngcol = lngcol + 1
        ws.Cells(4, "C").Copy ws1.Cells(lngrow, lngcol)
        lngcol = lngcol + 1
        ws.Cells(7, "B").Copy ws1.Cells(lngrow, lngcol)
        lngcol = lngcol + 1
        ws.Cells(50, "I").Copy ws1.Cells(lngrow, lngcol)
        lngcol = lngcol + 1
        ws.Cells(50, "H").Copy ws1.Cells(lngrow, lngcol)
        End If
        Wb.Close False
        Wbname = Dir
    Loop

    With Application
        .ScreenUpdating = True
        .EnableEvents = True
        .Calculation = lngCalc
    End With
End Sub

在repl中:

(defn map-with-reduce
  ([f coll] (seq (reduce #(conj %1 (f %2)) [] coll)))
  ([f coll & colls]
    (let [colls (cons coll colls)]
      (map-with-reduce (partial apply f)
                       (partition (count colls) 
                                  (apply interleave colls))))))

答案 2 :(得分:7)

真实地图在其集合参数上调用seq并返回一个懒惰的seq,所以也许这可以让它更接近真实地图?

select  id, 'home' as type, homeadd as address, hometel as tel from table1
Union all
select id, 'work' , workadd, worktel from table1

我会补充说,作为评论,但我没有声誉。 :)

答案 3 :(得分:2)

您可以使用conj附加到向量而不是前置到列表中:

(defn my-map [f coll]
  (reduce (fn [result item]
              (conj result (f item)))
          [] coll))

(my-map inc [1 2 3]) => [2 3 4] 

答案 4 :(得分:2)

反转结果更常见,而不是输入。当以递归方式处理单链表时,这是一种常见的习语。它保留了这种数据结构的线性复杂性。

您可能希望为其他seq提供不同的实现,例如:例如,矢量,可能基于conj而不是cons

通过这种练习,我不会太担心优雅

答案 5 :(得分:0)

正如已经指出的那样。您不必反转输入。 缺点是在序列的开头添加一个项目(甚至在向量上),而conj总是以最自然的方式添加,它总是以最快的方式为集合添加项目。它将从左到右添加列表,从左到右添加矢量。

我注意到大多数建议的答案都使用' reduce'所以请允许我主要使用递归建议这个:

origin/HEAD