首页 > 解决方案 > Clojure函数不返回地图

问题描述

我是 Clojure 和函数式编程的新手。我对 Clojure 中的数据结构(例如映射、列表和向量)有一个基本概念。

我正在尝试编写一个返回嵌套映射的函数。地图在函数内正确显示。下面的代码来自read-customer-file函数

([input-list new-map place-holder]
        (if (not-empty input-list)
            (do
                (def string-input (str (first input-list)))
                (def cust-id (get (first input-list) 0))
                (def cust-name (get (first input-list) 1))
                (def cust-address (get (first input-list) 2))
                (def cust-phone (get (first input-list) 3))
                (def temp-map (conj new-map {(keyword cust-id) {:cust-name cust-name, :cust-address cust-address, :cust-phone cust-phone}}))
                (read-customer-file (rest input-list) temp-map ())
            )
            (do 
                (map str new-map)
                ;(print (get-in new-map [:1 :cust-name]))
                (print new-map)
            )
        )
    )

这需要输入向量列表,如下所示:

([3 Fan Yuhong 165 Happy Lane 345-4533] [2 Sue Jones 43 Rose Court Street 345-7867] [1 John Smith 123 Here Street 456-4567])

并返回一个嵌套映射,如下所示:

{:3 {:cust-name Fan Yuhong, :cust-address 165 Happy Lane, :cust-phone 345-4533}, :2 {:cust-name Sue Jones, :cust-address 43 Rose Court Street, :cust-phone 345-7867}, :1 {:cust-name John Smith, :cust-address 123 Here Street, :cust-phone 456-4567}}

这就是我想要实现的目标,并且在该功能中运行良好。但是,如果我尝试使用返回类型的值在函数外部定义一个变量,我不会得到一个嵌套映射,而是一个字符串列表。为此,我只是删除了(print new-map)部分。

(do 
    (map str new-map)
)

并从函数定义外部调用它,如下所示:

(def customer-info-list read-customer-file)
(println (customer-info-list))

我得到的结果与我的预期不同,并且无法执行与地图相关的功能,例如getget-in

([:3 {:cust-name Fan Yuhong, :cust-address 165 Happy Lane, :cust-phone 345-4533}] [:2 {:cust-name Sue Jones, :cust-address 43 Rose Court Street, :cust-phone 345-7867}] [:1 {:cust-name John Smith, :cust-address 123 Here Street, :cust-phone 456-4567}])

我将非常感谢任何形式的帮助。我知道我的代码有点乱,我应该使用let而不是def变量名。但我刚刚开始使用 Clojure,这是我的第一个程序。

更新

我解决了这个问题。我所做的是将地图更改为函数内的排序地图。所以函数的最后一个返回将是一个排序的映射。

(do 
    (into (sorted-map) new-map)
)

但是,如果分配给变量,返回的值仍然是一个字符串。所以我再次将其转换为排序映射。然后它最终被转换为嵌套地图。

(def customer-info-list read-customer-file)
(def cust-map (into (sorted-map) (customer-info-list)))
(println cust-map)
(println (get-in cust-map [:1 :cust-name]))
(println (get cust-map :2))

上述三个打印语句的输出符合预期。

{:1 {:cust-name John Smith, :cust-address 123 Here Street, :cust-phone 456-4567}, :2 {:cust-name Sue Jones, :cust-address 43 Rose Court Street, :cust-phone 345-7867}, :3 {:cust-name Fan Yuhong, :cust-address 165 Happy Lane, :cust-phone 345-4533}}
John Smith
{:cust-name Sue Jones, :cust-address 43 Rose Court Street, :cust-phone 345-7867}

标签: clojurefunctional-programming

解决方案


您的代码中有许多错误,与缺乏基本的 clojure 知识有关:

1)不要使用def/defn内部函数,而是使用let

2)添加到地图通常是用assoc

3) 从函数中返回一些重要的值,并在外部对其进行自省总是更好。

我会像这样以更clojure的方式重写你的函数:(我故意保留placeholder参数,虽然它没有在你的代码中的任何地方使用,所以我无法推断它的目的)

(defn read-customer-file [input-list new-map placeholder]
  (if-let [[[id name address phone] & input-tail] (seq input-list)]
    (read-customer-file input-tail
                        (assoc new-map (keyword id)
                               {:cust-name name
                                :cust-address address
                                :cust-phone phone})
                        ())
    new-map))

在回复中:

user> (read-customer-file [["a" "b" "c" "d"] ["e" "f" "g" "h"]] {} ())
;;=> {:a {:cust-name "b", :cust-address "c", :cust-phone "d"},
;;    :e {:cust-name "f", :cust-address "g", :cust-phone "h"}}

但是您真正需要的是一些更高阶的数据处理功能,例如map

(defn read-customer-file [input-list placeholder]
  (into {}
        (map (fn [[id name address phone]]
               [(keyword id) {:cust-name name
                              :cust-phone phone
                              :cust-address address}])
             input-list)))

user> (read-customer-file [["a" "b" "c" "d"] ["e" "f" "g" "h"]] {})
;;=> {:a {:cust-name "b", :cust-phone "d", :cust-address "c"},
;;    :e {:cust-name "f", :cust-phone "h", :cust-address "g"}}

reduce

(defn read-customer-file [input-list placeholder]
  (reduce (fn [res [id name address phone]]
            (assoc res (keyword id) {:cust-name name
                                     :cust-phone phone
                                     :cust-address address}))
          {} input-list))

此外,将您的业务逻辑(在您的案例文件记录到客户转换中)抽象为一些专门的功能总是一个好主意,以保持处理功能更通用:

(defn file-rec->customer [[id name address phone]]
  [(keyword id) {:cust-name name
                 :cust-address address
                 :cust-phone phone}])

(defn read-data-file [record-processor input-list placeholder]
  (into {} (map record-processor) input-list))

user> (read-data-file file-rec->customer [["a" "b" "c" "d"] ["e" "f" "g" "h"]] {})
;;=> {:a {:cust-name "b", :cust-address "c", :cust-phone "d"},
;;    :e {:cust-name "f", :cust-address "g", :cust-phone "h"}}

推荐阅读