需要在Clojure中的函数末尾重新分配变量的值

需要在Clojure中的函数末尾重新分配变量的值,clojure,functional-programming,nlp,Clojure,Functional Programming,Nlp,我是Clojure世界和函数式编程的新手。我试图编写一个函数,计算给定一个词汇表(只是一个词汇表)和一组概率(每个单词出现的概率)的特定单词列表出现的概率。我使用一个简化的单词袋模型,假设每个结果都是独立的 例如,假设: 词汇(相关概率):睡眠(0.3),狗(0.09),a(0.2),牛(0.05),牛(0.17),船(0.04),一切(0.15) 句子:(列出“狗”船) 我想让它计算(0.05)*(0.09)*(0.04)=0.00018 我已经有了一个函数,它可以获取每个单词的概率,并且

我是Clojure世界和函数式编程的新手。我试图编写一个函数,计算给定一个词汇表(只是一个词汇表)和一组概率(每个单词出现的概率)的特定单词列表出现的概率。我使用一个简化的单词袋模型,假设每个结果都是独立的

例如,假设:

  • 词汇(相关概率):睡眠(0.3),狗(0.09),a(0.2),牛(0.05),牛(0.17),船(0.04),一切(0.15)
  • 句子:
    (列出“狗”船)
我想让它计算(0.05)*(0.09)*(0.04)=0.00018

我已经有了一个函数,它可以获取每个单词的概率,并且它可以按预期工作。我会将其粘贴到此处以供参考:

(defn lookup-probability [w outcomes probs]
  (if (not= w (first outcomes)) ;;if the current element is not equal to the word we're looking for...

    (lookup-probability w (rest outcomes) (rest probs)) ;;...keep cycling through the vocabulary

    (first probs) ;;once we find the right word, fetch the corresponding entry in the probability list
  )
)
以下是我感到困惑的部分:

(def sentenceprobs '()) ;;STEP 1
(defn compute-BOW-prob [sentence vocabulary probabilities]
  (if (not(empty? sentence))
      (def sentenceprobs (conj sentenceprobs (lookup-probability (first sentence) vocabulary probabilities)) ;;STEP 2
      (compute-BOW-prob (rest sentence) vocabulary probabilities) ;;STEP 3
    )
    (product sentenceprobs) ;;STEP 4 (the product function just multiplies all the elements of a list together)
  )
)
以下是我的总体策略:

  • 首先定义一个空列表“sentenceprobs”,我将在其中存储句子中每个单词的概率
  • 如果句子是非空的,则将第一个单词的概率添加到“sentenceprobs”列表中
  • 递归调用语句其余部分的函数(减去我们刚刚找到的单词ofc的概率)
  • 一旦句子为空,即我们获取了每个单词的概率,返回“sentenceprobs”中所有元素的乘积
  • 如果我只想使用这个函数一次,这个方法就可以了。然而,如果我想多次调用它,sentenceprobs仍然包含来自上一次调用的所有概率。该函数仍将运行,但它只是给了我错误的概率(小得多)因此我尝试在函数的最后重置sentenceprobs的值,使其“可重用”:

    (def sentenceprobs '())
    (defn compute-BOW-prob [sentence vocabulary probabilities]
      (if (not(empty? sentence))
          (def sentenceprobs (conj sentenceprobs (lookup-probability (first sentence) vocabulary probabilities))
          (compute-BOW-prob (rest sentence) vocabulary probabilities)
        )
        (product sentenceprobs)
      )
      (def sentenceprobs '()) ;; <---THIS IS WHAT I ADDED
    )
    
    (def sentenceprobs'())
    (defn compute BOW prob[句子词汇概率]
    (如果(不是(空句))
    (def sentenceprobs(conj sentenceprobs(查找概率(第一句话)词汇概率))
    (计算BOW prob(休息句)词汇概率)
    )
    (产品语句问题)
    )
    (def sentenceprobs'());;请看。它显示了我如何组织一个项目(只需克隆存储库并开始编码!)。特别是研究Clojure和命令式语言之间的差异

    我会通过使用映射来保存概率来解决这个问题。然后,您可以使用
    mapv
    (或只是
    map
    )将问题从映射中拉到向量(或列表)中。然后使用
    (apply*…)
    计算乘积:

    (ns tst.demo.core
      (:use tupelo.test)
      (:require
        [tupelo.core :as t]))
    
    (def prob-map
      {:sleep      0.3
       :dog        0.09,
       :a          0.2,
       :the        0.05,
       :cow        0.17,
       :boat       0.04,
       :everything 0.15})
    
    (defn calc-prob
      [words]
      (let [probs (mapv #(get prob-map %) words)]
        (apply * probs)))
    
    (dotest
      (let [sentence [:the :dog :boat]
            result   (calc-prob sentence)
            expected (t/spyx (* 0.05 0.09 0.04))  ; spyx displays the value
            ]
        (is (t/rel=  result expected :digits 8))))
    
    您可以通过以下方式运行它:

    > lein clean
    > lein test
    
    要产生输出:

    --------------------------------------
       Clojure 1.10.2-alpha1    Java 15
    --------------------------------------
    
    Testing tst.demo.core
    (* 0.05 0.09 0.04) => 1.7999999999999998E-4
    
    Ran 2 tests containing 1 assertions.
    0 failures, 0 errors.
    

    在Alan的回答的基础上构建一点。在这种情况下,你有一个值列表(一个句子中的单词),你想计算一个聚合(根据之前的概率计算,所有这些单词同时出现的概率)。我假设你像Alan一样构建了概率表(虽然我使用字符串而不是关键字作为键)

    要执行聚合,我们将使用
    reduce
    ,它允许您将集合折叠为单个值。它使用一个函数来完成此操作,该函数接受一个累加器和一个值,并将其应用于集合中的所有元素

    代码如下所示:

    (def prob-map
      {"sleep"      0.3
       "dog"        0.09,
       "a"          0.2,
       "the"        0.05,
       "cow"        0.17,
       "boat"       0.04,
       "everything" 0.15})
    
    (defn compute-BOW-prob [probs sentence]
      (reduce (fn [acc word]
                (* acc (get probs word 1)))
              1
              (clojure.string/split sentence #"\s")))
    
    (compute-BOW-prob prob-map "the dog boat")
    ;; => 1.7999999999999998E-4
    
    它本质上与Alan的解决方案相同,但它没有一个单独的步骤来乘以概率(这也为您节省了一个中间列表,在本例中这很可能不是问题,但如果您有非常大的输入,则可能是)

    上面的代码将概率图和一个句子作为输入。然后它拆分句子(我只是使用空格作为分隔符,但您可以根据需要添加标点符号和停止词),并使用提供的函数在列表中进行缩减。该函数使用累加器(
    acc
    )和列表中的一个元素(
    word
    ),并将累加器乘以该单词的概率(或1,如果找不到该单词……当然,您可以采取不同的方法来处理)。函数下方的
    1
    acc
    将采用的初始值

    这有助于澄清你的想法!
    一般来说,您根本不需要修改变量,并且绝对不应该在函数内部使用
    def
    。此外,尽量避免显式使用全局变量,并让函数将其作为参数。

    正如您所提到的,这不是使用
    def
    的方法。您可以尝试创建一个列表,然后在om多个断开连接的函数调用。这是命令式语言的方式,而不是函数式语言的方式。在这里,我们宁愿匿名创建一个列表,并将其作为函数的返回值传递

    (defn comp-bow [sentence vocabulary probabilities]
      (if (not (empty? sentence))
        (* (comp-bow (rest sentence) vocabulary probabilities)
           (lookup-probability (first sentence) vocabulary probabilities))
        1))
    
    我试着运行您的代码,但是
    computebow prob
    没有编译,所以我不确定您希望它如何工作

    无论如何,这里有一些改进点给你

    在第一个版本中,我尝试对原始设计进行尽可能少的修改(
    def
    )。 在您的设计中,您尝试仅在基本情况下返回产品(空句)。对于递归函数来说,这不是一个好的设计,它们应该始终返回相同类型的值。在
    comp bow
    中,这可以通过基本情况返回1和在调用函数中不断增加概率来解决

    (defn comp-bow [sentence vocabulary probabilities]
      (if (not (empty? sentence))
        (* (comp-bow (rest sentence) vocabulary probabilities)
           (lookup-probability (first sentence) vocabulary probabilities))
        1))
    
    我不知道你是否熟悉
    let
    。这是你在Clojure中分配东西的最后一步,分配只存在于
    let
    列表中。 此设计与您的设计非常相似,因为它创建了概率列表,并且只在最后执行乘法。(我使用
    apply*
    而不是
    产品(defn comp-bow3 [sentence vocabulary probabilities]
      (comp-bow3-sub sentence vocabulary probabilities 1))
    
    (defn comp-bow3-sub [sentence vocabulary probabilities product]
      (if (empty? sentence)
        product
        (recur (rest sentence) vocabulary probabilities
               (* product (lookup-probability (first sentence) vocabulary probabilities)))))