【问题标题】:Translate Java to Clojure - append to list and return list将 Java 转换为 Clojure - 附加到列表并返回列表
【发布时间】:2014-05-26 00:25:39
【问题描述】:

我想将以下 java 翻译成惯用的 clojure:

List<SimpleRecord> recs = new ArrayList<SimpleRecord>();
ParquetReader<SimpleRecord> reader = null;
    try {
      PrintWriter writer = new PrintWriter(Main.out, true);
      reader = new ParquetReader<SimpleRecord>(new Path(input), new SimpleReadSupport());
      for (SimpleRecord value = reader.read(); value != null; value = reader.read()) {
        //value.prettyPrint(writer);
        //writer.println();
        recs.add(value);
      }
      return value;
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (Exception ex) {
        }
      }
    }

但我在每一步都无法将元素推送到列表中。 这就是我现在拥有的:

(let [rows   (vector)
      reader (new parquet.hadoop.ParquetReader (new org.apache.hadoop.fs.Path "file.pq") 
                                               (new SimpleReadSupport))]
  ;reader  
  (try   
    (for [value (.read reader) :while (not (nil? value))]
      (conj value rows)) ;y u no recur?
    rows
    (finally (.close reader)))
  )

但我不知道在for 中这样做的惯用方式是什么。我猜我必须使用reduce 不?谢谢

【问题讨论】:

    标签: java clojure functional-programming


    【解决方案1】:

    对镶木地板或您想要做什么一无所知,这是我的最佳猜测和一些观察:

    (with-open [reader (parquet.hadoop.ParquetReader. 
                         (org.apache.hadoop.fs.Path. "file.pq") 
                         (SimpleReadSupport.))]
      (doall (for [value (repeatedly #(.read reader))
                   :while value] 
               value))
    

    nilfalse 是假的,其他一切都是真的,如果 value 永远不会是假的,那么只需使用 value 而不是 (not (nil? value))。 (额外的陷阱:(Boolean. false) 是真的,见https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L2607 :)

    它需要是一个向量,为什么?这里的代码返回一个seq,是doall提前实现的(要在with-open范围内,相当于你的try-finally。)

    Clojure 的数据是不可变的,您不需要附加到列表中,而是告诉它从旧数据中创建新数据并让 Clojure 处理细节 :-)。在您的代码示例中,您无法更改 rows 的值,一旦设置,就没有“变量”。有个可变引用类型,但在这里使用它们是不好的形式。从技术上讲,您可以使用循环/递归来实现这一点,但这里没有理由这样做(也许是性能),并且通常使用惰性序列方法确实有好处。

    reduce 不起作用,因为它消除了提前退出的能力,而且这里的输入是一个 java 对象,reduce 需要一个 seq-able 的东西,所以你仍然必须找到一些方法来生成一个懒惰的序列,你又回到了原点。

    for 不是循环,而是列表理解。它会生成一个惰性值序列。

    我不认为new 惯用,我不知道为什么:-)。不过,它是使用宏生成代码的更好选择。

    花时间理解这些概念,它们在实践中很棒!

    【讨论】:

    • reduce 可以通过reduced 提前退出。
    • 不应该在代码中重复出现,因为 (.read reader) 只返回一个值吗?
    • 我正在打一个java.lang.IllegalArgumentException: Don't know how to create ISeq from: parquet.tools.read.SimpleRecord;除了我在 seq 中的所有元素都是一样的!但是感谢您的详尽解释。
    • 只需将 (.read reader) 替换为 (repeatedly #(.read reader))
    • @SpiderPig 编程很难:-)
    【解决方案2】:

    gtrak 的方法当然是最好的,但我仍然想向您展示如何在 clojure 中编写显式循环。

    (with-open [reader (parquet.hadoop.ParquetReader. 
                         (org.apache.hadoop.fs.Path. "file.pq") 
                         (SimpleReadSupport.))]
      (loop [rows []]
        (let [value (.read reader)]
          (if value
              (recur (conj rows value))
              rows))))
    

    【讨论】:

      【解决方案3】:

      Clojure 中的for 不是 for 循环,它是一个序列理解。

      默认的 Clojure 数据结构是不可变的,应该迭代构建,而不是在迭代中发生变异。

      (with-open [reader (parquet.hadoop.ParquetReader. 
                           (org.apache.hadoop.fs.Path. "file.pq") 
                           (SimpleReadSupport.))]
        (doall
         (take-while (complement nil?)
                     (repeatedly (fn []
                                   (try (.read reader)
                                        (catch Exception e))))))
      

      【讨论】:

      • 善用line-seq。 :-)
      • 谢谢。我公然复制/粘贴了您对 new / with-open 的删除 :)
      • 啊,我只是看了一眼javadoc,ParquetReader没有扩展java.io.reader。也许将其切换为使用 iterate/take-while 只是为了替代(假设他的基本情况首先是正确的)。
      • 已更新,谢谢(如果只有格式化程序理解 #() 表示法)
      猜你喜欢
      • 1970-01-01
      • 2016-05-10
      • 1970-01-01
      • 2023-01-10
      • 2013-07-21
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多