首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >这个‘`doseq`’语句和‘the’语句之间有什么区别?

这个‘`doseq`’语句和‘the’语句之间有什么区别?
EN

Stack Overflow用户
提问于 2015-03-28 01:01:16
回答 2查看 1.2K关注 0票数 3

如果你一整天都在听我的问题,

我正在clojure中做一个类项目,在读取文件、解析文件和根据其内容创建图表时遇到困难。我已经成功地打开并读取了一个文件,并根据需要解析了行。我现在面临的问题是根据读取的数据创建一个图形结构。

先了解一些背景。在我在这个项目中实现的其他函数中,我使用了一个for语句来“构建”这样的值列表。

代码语言:javascript
复制
...
(let [rem-list (remove nil? (for [j (range (count (graph n)))]
    (cond (< (rand) 0.5)
        [n (nth (seq (graph n)) j)])))
...

这个for将建立一个从图中删除的边列表,在完成之后,我可以在reduce中使用rem-list来删除某个图结构中的所有边。

回到我的问题上。我想,如果我逐行读取一个文件,我可以以同样的方式“构建”一个列表,因此我实现了以下功能

代码语言:javascript
复制
(defn readGraphFile [filename, numnodes]
  (let [edge-list 
        (with-open [rdr (io/reader filename)]
          (doseq [line (line-seq rdr)]
           (lineToEdge line)))]
    (edge-list)))

尽管如果我要运行这个函数,我最终会出现一个空指针异常,就好像没有向edge-list“添加”任何东西一样。所以做个懒鬼/好人?程序员,我是,我很快想到了另一种方式。尽管它仍然在某种程度上依赖于我对for如何构建列表的思考。

在这个函数中,首先,let [graph等于一个具有已知节点数的空图。然后,每次读取一行时,我只需将该边(文件中的每一行都是边)添加到图形中,实际上是“构建”我的图形。该函数如下所示

代码语言:javascript
复制
(defn readGraph [filename, numnodes]
  (let [graph (empty-graph numnodes)]
    (with-open [rdr (io/reader filename)]
      (doseq [line (line-seq rdr)]
        (add-edge graph (lineToEdge line))))
    graph))

这里,lineToEdge返回一对数字(例如[1 2])。这是add-edge函数的适当输入。

代码语言:javascript
复制
finalproject.core> (add-edge (empty-graph 5) (lineToEdge "e 1 2"))
[#{} #{2} #{1} #{} #{}]

但是,这个函数的问题是,它似乎从来没有向图中添加过一个边。

代码语言:javascript
复制
finalproject.core> (readGraph "/home/eccomp/finalproject/resources/11nodes.txt" 11)
[#{} #{} #{} #{} #{} #{} #{} #{} #{} #{} #{}]

所以我想我的问题在于doseqfor有什么不同?它是不同的,还是我的实现不正确?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-03-28 01:19:53

doseqfor的不同之处在于,它的目的是在序列上运行一个函数,只是为了产生副作用。

如果您查看doseq:(https://clojuredocs.org/clojure.core/doseq)的文档

按照" for“提供的绑定和过滤,反复执行主体(可能是副作用)。不会保留序列的头。回报为零

因此,不管您正在做什么处理,nil都将被返回。

您可以使用doseq切换for,它应该可以工作。但是,line-seq很懒,所以您可能要做的是将它包装在一个doall中,以确保它在打开文件时会尝试读取所有的行。

另外,第二个readGraph函数只返回一个空图:

代码语言:javascript
复制
(defn readGraph [filename, numnodes]
  (let [graph (empty-graph numnodes)]
    (with-open [rdr (io/reader filename)]
      (doseq [line (line-seq rdr)]
        (add-edge graph (lineToEdge line))))
    graph))

最后一行只是您使用let设置的空图,因为Clojure是一种不变的语言,图形引用永远不会更新,因为您有一个函数,它接受一个现有的图形并向它添加一个边缘,因此在传递正在构建的列表时,您需要对列表进行一步遍历。

我知道一定有更好的方法来做这件事,但我不像我想要的那样擅长Clojure,而是这样的:

代码语言:javascript
复制
(defn readGraph
  [filename numnodes]
  (with-open [rdr (io/reader filename)]
    (let [edge-seq (line-seq rdr)]
        (loop [cur-line (first edge-seq)
               rem-line (rest edge-seq)
               graph (empty-graph numnodes)]
          (if-not cur-line
            graph
            (recur (first rem-line)
                   (rest rem-line)
                   (add-edge graph (lineToEdge cur-line))))))))

可能会让你更接近你想要的东西。

再考虑一下,您可以尝试使用减缩方法,因此:

代码语言:javascript
复制
(defn readGraph
  [filename numnodes]
  (with-open [rdr (io/reader filename)]
    (reduce add-edge (cons (empty-graph numnodes)
                           (doall (line-seq rdr))))))

还原将通过一个序列,将传入的函数应用到前两个参数,然后将其结果作为第一个参数传递给下一个调用。cons在那里,所以我们可以确定一个空图是传入的第一个参数。

票数 5
EN

Stack Overflow用户

发布于 2015-03-28 01:22:03

您可以很容易地在Clojure文档中找到问题的答案。

您可以在clojuredocs.org网站上找到所有核心功能的完整文档,也可以简单地在Clojure REPL中运行(doc <function name>)

以下是功能文档所说的话:

代码语言:javascript
复制
=> (doc doseq)
(doc doseq)
-------------------------
clojure.core/doseq
([seq-exprs & body])
Macro
  Repeatedly executes body (presumably for side-effects) with
  bindings and filtering as provided by "for".  Does not retain
  the head of the sequence. Returns nil.

换句话说,总是返回nil。因此,您可以使用它的唯一方法是产生一些副作用(例如,重复打印到您的控制台)。

以下是功能文档所说的话:

代码语言:javascript
复制
=> (doc for)
(doc for)
-------------------------
clojure.core/for
([seq-exprs body-expr])
Macro
  List comprehension. Takes a vector of one or more
   binding-form/collection-expr pairs, each followed by zero or more
   modifiers, and yields a lazy sequence of evaluations of expr.
   Collections are iterated in a nested fashion, rightmost fastest,
   and nested coll-exprs can refer to bindings created in prior
   binding-forms.  Supported modifiers are: :let [binding-form expr ...],
   :while test, :when test.

  (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))

因此,for函数产生一个惰性序列,可以绑定到某个变量并在代码中稍后使用。

请注意,产生的序列是懒惰的。这意味着在尝试使用(或打印)这些元素之前,不会计算这个序列的元素。例如,以下功能:

代码语言:javascript
复制
(defn noop []
  (for [i (range 10)]
    (println i))
  nil)

不会打印任何内容,因为没有使用for循环结果,因此也没有计算结果。您可以使用函数强制对惰性序列进行计算。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29312449

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档