“无法解析符号”错误

时间:2009-11-21 21:12:51

标签: functional-programming lisp clojure

当我将此代码粘贴到REPL中时,它可以正常工作:

(use 'clojure.contrib.seq-utils)
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
  "Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
  []
  (let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
    (apply str (flatten (map (fn [coll] (map eval coll)) password)))))

现在,我有:gen-class :implements [my.ServiceInterface]的代码和一个前缀为-的函数来实现接口。我使用Maven / Groovy / TestNG进行单元测试。其他一些接口/ Clojure实现一切正常,但在这种特殊情况下,我得到了这个错误:

java.lang.RuntimeException:
java.lang.Exception: Unable to resolve symbol: random-letter in this context (NO_SOURCE_FILE:32)

我无法弄清楚原因。我唯一能说的是这个函数与所有其他函数的不同之处在于,这是我使用引号的唯一地方,即'((random-digit))'(random-letter)。编辑:此外,这是我使用eval的唯一地方。

我尝试将这些功能定义为“非私人”(defn而不是defn-)。我还尝试了顶部的(declare random-digit random-letter)。这些都没有解决问题。

另外,如果您有更好的方法来实施random-password功能,我很满意。但是我仍然想知道为什么我会收到这个错误以及如何让它发挥作用。

非常感谢您的帮助。 Clojure太棒了。

编辑:这是完整的代码。

(ns fred.hp2010.service.ClojurePoolerService
  (:gen-class :implements [fred.hp2010.service.PoolerService])
  (:use [clojure.contrib.seq-utils :only (flatten)]))

(def dao (fred.hp2010.persistence.Repository/getDao))

(declare find-by is-taken random-password)

(defn -addPooler [this pooler] (. dao insert "POOLER" pooler))
(defn -getPoolers [this] (. dao list "poolers"))
(defn -isEmailTaken [this email] (is-taken {"email" email}))
(defn -isUsernameTaken [this username] (is-taken {"username" username}))
(defn -login [this email password] (. dao findSingle "POOLER" {"email" email "password" password}))

(defn -changePassword [this email new-password]
  (let [updated-pooler (assoc (into {} (find-by {"email" email})) "password" new-password)]
    (. dao update "POOLER" "POOLER_ID" updated-pooler)))

(defn -resetPassword [this email]
  (let [new-password (random-password)]
    (-changePassword this email new-password)
    new-password))

(defn- find-by [params] (. dao findSingle "POOLER" params))
(defn- is-taken [params] (not (nil? (find-by params))))

(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
  "Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
  []
  (let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
    (apply str (flatten (map (fn [coll] (map eval coll)) password)))))

2 个答案:

答案 0 :(得分:7)

我不知道为什么你在使用:gen-class编译时出现问题,但如果eval与它有关,我也不会感到惊讶。 eval通常是一个坏主意。尝试(完全未经测试)的一件事是使用`(反引号)而不是'(引用),以便您的符号是名称空间限定的。不知道这是否有帮助。

虽然可能更好地摆脱eval。如果您通过repeatedly将随机字符函数转换为无限延迟seqs,您可以这样做:

(defn- random-letter [] (repeatedly #(char (+ (rand-int 26) 97))))
(defn- random-digit  [] (repeatedly #(rand-int 10)))
(defn- random-password
  "Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
  []
  (apply str
         (mapcat (fn [[n f]] (take n (f)))
                 [[2 random-letter]
                  [1 random-digit]
                  [2 random-letter]
                  [1 random-digit]
                  [2 random-letter]])))

答案 1 :(得分:3)

在前面几行中,我在遵循语法方面遇到了一些麻烦。特别是,为什么第7行的所有报价?延迟评估所有这些表达可能对你没有帮助。我猜猜引用的'(random-letter)会破坏你的乐趣。

您可以在避开eval时编写更简单的代码。我打算在REPL中试一试,我希望很快能回来改进版本。

修改

好的,这有效:

(apply str (interpose (random-digit) (repeat 3 (apply str (repeat 2 (random-letter))))))

......它不需要clojure.contrib:)的任何内容。

str函数将任何参数混合成一个字符串。如果参数位于列表中,则使用str在列表中隐藏apply

正如你所说,Clojure很酷!

修改

这是一个根据字符串规范生成随机字符串和数字的函数:

(apply str (map (fn [c] (if (= c \a) (random-letter) (random-digit))) "aanaanaa")))

它与您的规格略有不同,但我认为这很酷。