Description:
A bookseller has lots of books classified in 26 categories labeled A, B, ..., Z
. Each book has a code c
of 3, 4, 5 or more characters. The 1st character of a code is a capital letter which defines the book category.
In the bookseller’s stocklist each code c
is followed by a space and by a positive integer n (int n >= 0) which indicates the quantity of books of this code in stock.
For example an extract of a stocklist could be:
L = {"ABART 20", "CDXEF 50", "BKWRK 25", "BTSQZ 89", "DRTYM 60"}.
or
L = ["ABART 20", "CDXEF 50", "BKWRK 25", "BTSQZ 89", "DRTYM 60"] or ....
You will be given a stocklist (e.g. : L) and a list of categories in capital letters e.g :
M = {"A", "B", "C", "W"}
or
M = ["A", "B", "C", "W"] or ...
and your task is to find all the books of L with codes belonging to each category of M and to sum their quantity according to each category.
For the lists L and M of example you have to return the string (in Haskell/Clojure/Racket a list of pairs):
(A : 20) - (B : 114) - (C : 50) - (W : 0)
where A, B, C, W are the categories, 20 is the sum of the unique book of category A, 114 the sum corresponding to “BKWRK” and “BTSQZ”, 50 corresponding to “CDXEF” and 0 to category ‘W’ since there are no code beginning with W.
If L or M are empty return string is ""
(Clojure and Racket should return an empty array/list instead).
Note:
In the result codes and their values are in the same order as in M.
测试案例
listOfArt = c("BBAR 150", "CDXE 515", "BKWR 250", "BTSQ 890", "DRTY 600", "XRAV 1")
listOfCat = c("A", "B", "C", "D")
stockList(listOfArt,listOfCat)
[1] "(A : 0) - (B : 1290) - (C : 515) - (D : 600)"
我的代码
stockList <- function(listOfArt, listOfCat) {
if (length(listOfArt)*length(listOfCat) == 0) return("")
result.split <- sapply(listOfArt, function(bookcode){return(list(name=unlist(strsplit(bookcode, split=""))[1],num=as.integer(gsub('[^0-9]',"", bookcode))))})
paste0(sapply(listOfCat, function(cat){paste0(c("(",cat," : ",sum(unlist(result.split[2,grep(cat,result.split[1,])])),")"),collapse = "")}),collapse = " - ")
}
这段代码有以下几点需要解释说明一下:
- 在计算
result.split
的过程中,由于我的构想是对每一个listOfArt
中的字符串,都能够返回首字母以及当中的数字,这就有两个返回了。这里我觉得sapply
函数比较好的一点是,它会把函数返回的列表中所有的第一个元素放在一起,同理第二个、第三个等也会相应地放到一起,这就是之后的调用变得非常方便,比如调用所有的name
只需result.split[1, ]
即可。 - 在用
strsplit
函数处理完字符串之后,还需要使用unlist
函数将得到的结果转为向量,当然也可以用strsplit()[[1]]
调用,需要注意的是,一定要是’[[1]]’,里面不能是1以外的数字。 gsub
函数的第一个参数是'[^0-9]'
,这个是正则表达式,目的是在函数第三个参数中匹配非0-9的内容,并将它们替换为第二个参数的""
,进而整个字符串将只剩下0-9的数字,接着使用as.integer
将得到的数字字符转为数字。- 再讲讲
grep
函数,这个函数会返回匹配的所有结果对应的下标索引。而grepl
则是所有匹配结果都为TRUE
,其余的则为FALSE
,得到一个与被匹配对象同长度的逻辑值向量。此外,match
函数是返回匹配出的第一个结果的下标索引,这与grep
有所不同。 - 最后讲讲
paste
函数,这个函数有两个需要注意的参数——seq
和collapse
,seq
参数是设置字符合并时两个字符之间用什么隔开,paste0
函数默认seq=""
,也就是合并的字符之间是无缝连接的。collapse
参数设置的是是否应该将所有字符合并的结果再进一步地综合到一个字符串中去,如果综合到一起,应该用什么去分割开所有的字符。在本问中我将collapse
参数设置为-
,意思是将得到的所有字符合并结果再用-
连接起来变成一个字符串。
别人的代码
stockList <- function(L, M) {
if (length(L)*length(M) == 0) return("")
r <- sapply(M, function(v) sprintf("(%s : %s)",v, sum(as.numeric(gsub("\\D+","",L[grep(paste0("^",v),L,perl = TRUE)])))))
paste0(r,collapse = " - ")
}
这里让我比较惊讶的是直接用sprintf
的结果竟然可以作为函数的返回值,其次是直接使用^character
的形式匹配首字母为character
的字符串,这比我的代码效率高多了,需要注意的是,这里面的perl = TRUE
可以去除的,得到的结果会是一样的。