来自Clojure的新手提问。任务很简单,但我很难找到最好的方法 - 我需要设置输入,用户可以给我一个列表(用户应该确定多长时间)的自然数,程序应该只返回这些数字的总和。
也许这已经完全错了:
(defn inputlist[naturallist]
(println "Enter list of natural numbers:")
(let[naturallist(read-line)] ))
答案 0 :(得分:4)
这是一种方法:
> lein new app demo
> cd demo
修改project.clj
和src/demo/core.clj
,使其如下所示:
> cat project.clj
(defproject demo "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/tools.reader "1.1.3.1"] ]
:main ^:skip-aot demo.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
> cat src/demo/core.clj
(ns demo.core
(:require
[clojure.tools.reader.edn :as edn]
[clojure.string :as str] ))
(defn -main []
(println "enter numbers")
(let [input-line (read-line)
num-strs (str/split input-line #"\s+")
nums (mapv edn/read-string num-strs)
result (apply + nums) ]
(println "result=" result)))
结果
> lein run
enter numbers
1 2 3 <= you type this, then <enter>
input-line => "1 2 3"
num-strs => ["1" "2" "3"]
nums => [1 2 3]
result => 6
您可能也希望开始查看一些初学者书籍:
答案 1 :(得分:2)
我能想到的最快捷的方法是
#(apply + (map #(Integer/parseInt %) (re-seq #"\d+" (read-line))))
这定义了一个匿名函数:
\d+
搜索连续数字的字符串。将每个连续数字串添加到序列中,随后返回该序列。因此,例如,如果您输入123, 456, 789
,re-seq
函数将返回序列'("123" "456" "789")
。#(Integer/parseInt %)
调用返回的列表中的每个元素调用匿名函数re-seq
,创建另一个结果列表。因此,如果输入为'("123" "456" "789")
,则输出将为'(123 456 789)
。+
函数应用于数字列表,将它们相加,然后返回总和。瞧瞧!最终结果是,您键入一串数字,用非数字字符分隔,您可以获得这些数字的总和。如果你想要更整洁,促进代码重用,并且通常使这更有用,你可以将其分解为单独的函数:
(defn parse-string-list [s]
(re-seq #"\d+" s))
(defn convert-seq-of-int-strings [ss]
(map #(Integer/parseInt %) ss))
(defn sum-numbers-in-seq [ss]
(apply + ss))
以Lisp-y方式调用它看起来像
(sum-numbers-in-seq (convert-seq-of-int-strings (parse-string-list (read-line))))
或者,以更多的Clojure-y方式
(-> (read-line)
(parse-string-list)
(convert-seq-of-int-strings)
(sum-numbers-in-seq))
祝你好运。
答案 2 :(得分:1)
欢迎来到Clojure和StackOverflow!
以下是如何操作:
(defn input-numbers-and-sum []
(print "Enter list of natural numbers: ")
(flush)
(->> (clojure.string/split (read-line) #"\s+")
(map #(Integer/parseInt %))
(reduce +)))
以下是它的工作原理:
调用print
而不是println
可以避免在行尾打印换行符。这样,用户的输入将与提示符显示在同一行。
由于没有换行符,您必须调用flush
强制打印包含提示的输出缓冲区。
split
将用户输入的内容拆分为一系列字符串,分为正则表达式匹配的位置。您必须说clojure.string/split
而不仅仅是split
,因为split
不在Clojure的core library中。 clojure.string/
指定the library。 #"\s+"
是regular expression,可以匹配任意数量的连续空白字符。因此,如果您的用户输入" 6 82 -15 "
,split
将返回["6" "82" "-15"]
。
map
在每个字符串上调用标准Java库函数Integer.parseInt
。 Integer/parseInt
是Clojure的Java interop语法,用于调用Java类的静态方法。 #(...)
是简洁的语法,用于定义匿名函数; %
是传递给该函数的参数。因此,给定上面的字符串序列,对map
的调用将返回一个整数序列:[6 82 -15]
。
reduce
在整数序列的每个元素上重复调用+
函数,将总和作为参数与下一个整数一起传递。 map
和reduce
实际上有三个论点;下一段说明如何填写第三段。
->>
是“线程最后一个宏”。它重写其中的代码,传递每个表达式的输出,但最后一个作为下面表达式的最后一个参数。结果是:
(reduce + (map #(Integer/parseInt %) (clojure.string/split (read-line) #"\s+")))
大多数人发现->>
更容易阅读的版本。
做一些非常简单的事情似乎很多,但是一旦你习惯了Clojure,它实际上就是面包和黄油。 Clojure旨在使事物易于组合; map
,reduce
和->>
是将其他功能连接在一起的特别有用的工具。
我已经包含了文档的链接。值得一看;许多都包含典型的使用示例。
还有其他解析数字的方法,当然,其中一些方法会显示在this question的答案中。我上面写的是一种“惯用”的方式。了解这一点,您将了解Clojure中许多日常必备技术。
答案 3 :(得分:0)
是的,readline是正确的方法。
但是readlines
中的每个元素本质上都是java.lang.Character
的一个实例,并且因为你想要总和,所以你更喜欢在对list的元素求和之前将它们转换为整数。
(defn input-list
[]
(print "Enter list of natural numbers")
(let [nums (read-line)]
(reduce + (map #(Integer/parseInt %) (clojure.string/split nums #"\s+")))
这可能不是最惯用的方法,但可以随意调整它。
另外,请清理变量/函数名称。
编辑:(Integer/parseInt %)
如果直接使用可能会导致错误,因为输入是字符的实例,而不是字符串。因此,我们可以使用clojure.string/split
将用户输入转换为字符串序列,并使用Integer/parseInt %
进行转换。
事实上,可以使用线程优先的宏编写更易读的版本:
(defn input-list []
(print "Enter list of natural numbers: ")
(->> (clojure.string/split (read-line) #"\s+")
(map #(Integer/parseInt %))
(reduce +)))
这是一种更为完美的方式。
答案 4 :(得分:0)
如果您不关心负面情况(错误的输入,语法问题),最快的解决方案是评估用户输入的内容:
(defn sum-ints []
(let [input (read-line)
ints (read-string (str "(" input ")"))]
(reduce + 0 ints)))
用法:
user=> (sum-ints)
1 2 3 4 5
15
read-string
函数在这种情况下评估文本表达式(1 2 3 4 5)
。由于数字文字变成数字,结果将只是一个数字列表。