我是一名相当新的软件工程专业的学生,所以我的知识有限。 我已经获得了对数组进行排序的任务,在这种情况下,这是一组彩票号码,可以在控制台中提供类似输出的图形,我可以使用一大堆match语句来做到这一点,但是感觉必须有更好的方法
这是给出的确切摘要:
假设下面的声明定义了在指定时期内抽出每个国家彩票球(1 ..49)的次数。
var lottery = Array(23,16,18,19,26,13,22, /* 1 .. 7 */
20,14,22,18,21,15,17, /* 8 .. 14 */
24,15,18,20,13,14,20, /* 15 .. 21 */
18,22,20,16,19,11,20, /* 22 .. 28 */
16,28,22,20,15,17,17, /* 29 .. 35 */
21,21,19,20,14,22,25, /* 36 .. 42 */
19,17,26,18,20,23,12); /* 43 .. 49 */
编写程序来打印直方图,从而使用星形图形显示信息,如下所示:
1(23)| **********************
2(16)| ************
以此类推。
关于如何完成此操作的任何提示/建议将不胜感激,因为到目前为止,它使我感到困惑。我并不是在寻求确切的解决方案,而只是寻求一些指导,说明我可以使用哪些方法来实现这些目标,因为我可能没有碰到它们。 谢谢,祝你有美好的一天。
编辑-我完成任务的第一种方式。
object lottery {
def main(args: Array[String]): Unit = {
var lotteryIndex = 1
var lottery = Array(23,16,18,19,26,13,22, /* 1 .. 7 */
20,14,22,18,21,15,17, /* 8 .. 14 */
24,15,18,20,13,14,20, /* 15 .. 21 */
18,22,20,16,19,11,20, /* 22 .. 28 */
16,28,22,20,15,17,17, /* 29 .. 35 */
21,21,19,20,14,22,25, /* 36 .. 42 */
19,17,26,18,20,23,12); /* 43 .. 49 */
scala.util.Sorting.quickSort(lottery)
var i = 1
while(i < lottery.length){
if(lottery(i) != lottery(i-1)){
print("\n" + lotteryIndex + " (" + lottery(i) + ") | " + "*")
i += 1
lotteryIndex += 1
}else{
print("*")
i += 1
}
}
}
}
答案 0 :(得分:3)
这很简单。数组中的每个值都是索引的频率计数,因此您需要能够创建一个星号('*')字符串,其长度与频率计数相同。这可以通过多种方法来实现,但是也许最简单的方法是定义一个函数,如下所示:
def ast(fc: Int) = "*" * fc
其中fc
是频率计数和所需的星号数。因此,例如,如果fc
为5,则此表达式的结果将为"*****"
。
接下来,我们需要一个函数来创建输出行,给定一个球号(bn
)和相应的频率计数(fc
):
def line(bn: Int, fc: Int) = f"$bn%2d ($fc%2d) | ${ast(fc)}%s"
让我们解释一下这里发生了什么。字符串的f
前缀告诉 Scala 它包含字符串格式信息,必须进行插值。 (有关更多信息,请参阅this explanation。)
无论何时遇到$
,以下内容都将被视为需要在输出中转换为字符串的表达式。如果表达式不是简单的表达式,例如变量或值名称(例如,如果是函数调用),则表达式必须用大括号括起来(例如${...}
)。如果%
跟随表达式,则它以类似于C language std::printf
function的方式标识表达式的格式。
因此:$bn%2d
将整数值bn
转换为两位十进制字符串; $fc%2d
将整数值fc
转换为两位十进制字符串;并且${ast(fc)}%s
调用函数ast
,并传递fc
作为参数,然后将结果字符串插入输出。 (在后一种情况下,%s
是多余的。)
(注意:您对原始问题的评论之一指定了%2s
的格式。虽然此格式将输出正确地格式化为两个字符,但它不会检查参数类型是否为整数,因此是,不推荐。)
现在,我们需要遍历数组以为数组的每个成员调用后一个函数...
首先,请注意,我们需要索引和值(球数是数组索引+ 1,因为数组是从0开始的)。第一步是通过lottery
方法将Array[Int]
从Array[(Int, Int)]
转换成zipWithIndex
。这将获取lottery
数组中的每个索引值对,并将它们组合为一个 tuple (值先出现,然后是索引),然后将结果放入另一个数组。
然后我们可以将结果中的每个元组值map
移至输出行,如下所示:
val lines = lottery.zipWithIndex.map {
case (fc, idx) => line(idx + 1, fc)
}
以上是使用部分函数断开元组的一种方法。如果您不介意使用元组访问成员,也可以使用以下方法更简洁(但也更加混淆):
val lines = lottery.zipWithIndex.map(p => line(p._2 + 1, p._1))
其中p
是数组索引和频率计数的元组。选择您喜欢的方法。
最后,我们可以迭代结果以打印出结果的每一行:
lines.foreach(println)
后者是以下内容的简写:
lines.foreach(l => println(l))
因此,将所有这些放在一起,我们得到以下结果:
object Lottery
extends App {
// Lottery data
val lottery = Array(23,16,18,19,26,13,22, /* 1 .. 7 */
20,14,22,18,21,15,17, /* 8 .. 14 */
24,15,18,20,13,14,20, /* 15 .. 21 */
18,22,20,16,19,11,20, /* 22 .. 28 */
16,28,22,20,15,17,17, /* 29 .. 35 */
21,21,19,20,14,22,25, /* 36 .. 42 */
19,17,26,18,20,23,12) /* 43 .. 49 */
// Get a string of asterisks of the required length.
def ast(fc: Int) = "*" * fc
// Get a line of the histogram from a ball number and frequency count.
def line(bn: Int, fc: Int) = f"$bn%2d ($fc%2d) | ${ast(fc)}%s"
// Get each line of output for the histogram.
val lines = lottery.zipWithIndex.map {
case (fc, idx) => line(idx + 1, fc)
}
// Print each line of the histogram
lines.foreach(println)
}
请注意,我们尚未对直方图进行排序(目前的代码与您摘要中指定的输出匹配)。如果您需要按频率计数的降序列出球,只需使用频率计数对压缩数组进行排序即可:
val lines = lottery.zipWithIndex.sortBy(-_._1).map {
case (fc, idx) => line(idx + 1, fc)
}
sortBy
将一个函数用作排序值。在这里,我们将频率计数取为负,以反转排序的顺序(如果您希望以频率计数的升序进行排序,请删除-
号)。
请注意,我们必须在对带有索引的压缩后的数组进行排序,否则我们将失去球号关联。
一些进一步的观察结果
var
。请改用val
。 (可以编写从不完全不使用var
的代码。)object
(除package object
之外)通常应按惯例大写首字母。scala.App
可使对象的构造函数成为程序的main
方法,并简化事务。未排序的输出(与您的摘要匹配):
1 (23) | ***********************
2 (16) | ****************
3 (18) | ******************
4 (19) | *******************
5 (26) | **************************
...
排序输出:
30 (28) | ****************************
5 (26) | **************************
45 (26) | **************************
42 (25) | *************************
15 (24) | ************************
...
更新
为回应您有关需要频率计数中的频率计数的评论(如果我理解正确的话),请按以下步骤操作:
要获得每个值的频率,有(再次)多种方法可以执行此操作。在这种情况下,我将使用foldLeft
操作。
您可以将foldLeft
视为累加操作:第一个参数标识零值- accumulator 的初始值,而第二个参数是应用于容器的每个成员(Array
)的函数。后一个函数本身带有两个参数:当前累加器值和当前元素的值,并返回新的累加器值。
在这种情况下,我将使用foldLeft
来建立一个关联数组(一个SortedMap
,它的键会以升序排列)将每个频率计数链接到该次数它发生。 (在此SortedMap
中,_key_s是频率计数,与每个键关联的值是该频率计数的频率。)
这是下面的样子(我将其分解为多个步骤以使其更易于理解):
// Required import.
import scala.collection.immutable.SortedMap
// Our initial, empty map, which reports a frequency of zero if a key is not present.
// Note that a `SortedMap` keeps its keys sorted.
val zero = SortedMap.empty[Int, Int].withDefaultValue(0)
// Iterate through the frequency counts (`fc`) in the `lottery` array. `fm` is the current
// status of our map.
val freq = lottery.foldLeft(zero) {(fm, fc) =>
// Determine the new count for this frequency count.
val newCount = fm(fc) + 1
// Create an association from the frequency count to this new count.
val assoc = fc -> newCount
// Add this association to the map, resulting in a new map. Any existing association
// will be replaced.
fm + assoc
}
如果您已遵循此操作,则为简短版本:
val freq = lottery.foldLeft(SortedMap.empty[Int, Int].withDefaultValue(0)) {(fm, fc) =>
fm + (fc -> (fm(fc) + 1))
}
现在剩下的就是创建直方图并打印它们:
val lines = freq.map {
case (k, v) => line(k, v)
}
lines.foreach(println)
(注意:line
方法的参数定义需要根据更改进行调整,但是行为是相同的。)
输出:
11 ( 1) | *
12 ( 1) | *
13 ( 2) | **
14 ( 3) | ***
15 ( 3) | ***
16 ( 3) | ***
17 ( 4) | ****
18 ( 5) | *****
19 ( 4) | ****
20 ( 8) | ********
21 ( 3) | ***
22 ( 5) | *****
23 ( 2) | **
24 ( 1) | *
25 ( 1) | *
26 ( 2) | **
28 ( 1) | *