我所做的(在朋友的帮助下)创建了一个函数,该函数对索引采用var getSongs = function(number, year, db, callback) {
var collection = db.collection('songsList');
var songIds = [];
var chartSongs = [];
for (var i = 0; i < number; i++) {
// Get one random document from the collection.
collection.aggregate([
{
$match: { '_id': { '$nin': songIds }, 'songYear': year }
},
{
$sample: { size: 1 }
}
],
function(err, chart) {
songIds.push(chart[0]._id);
chartSongs.push(chart[0]);
if (songIds.length === number) {
callback(chartSongs);
}
})
}
};
,List
,并在以下函数中应用函数指定的索引。它与Int
类似,但不是将函数应用于每个元素,而是仅将其应用于一个元素。
所以我的问题是:
以下是代码:
Map
注意:Elm discourages indexing Lists, as they are linked lists under the hood: to retrieve the 1001th element, you have to first visit all 1000 previous elements.尽管如此,如果你想这样做,这是一种方式。
答案 0 :(得分:1)
List.indexedMap
是一个很好的方式来做你正在描述的事情。
然而,既然你提到了必须访问列表中所有前面元素的缺点,那么你的例子中的实际情况实际上有点糟糕,如果你真的对性能非常担心。
无论索引是否存在,您的列表实际上至少完全遍历了两次。询问链表长度的简单行为必须遍历整个列表。查看源代码length
is implemented in terms of a foldl
。
此外,List.indexedMap
至少遍历整个列表一次。我说,至少一次,因为除了使用indexedMap
之外,map
的来源还calls the length
function。如果我们很幸运,length
电话会被记忆(我对Elm内部不熟悉,不知道是否存在,因此至少评论)。 map
本身在被调用时遍历整个列表,不像Haskell懒惰地评估事物,只是必要时。
如果您使用indexedMap
,则无论您感兴趣的位置如何,都会对整个列表编制索引。也就是说,即使您要在索引0处应用该函数,也会对整个列表编制索引。
如果您确实希望将遍历次数减少到最少,那么(此时)您必须实现自己的功能,而您必须在不依赖length
的情况下执行此操作。 indexedMap
。
这是一个changeAt
函数的示例,它避免了不必要的遍历,如果找到了位置,它就会停止遍历列表。
changeAt : List a -> Int -> (a -> a) -> Result String (List a)
changeAt l i func =
if i < 0 then
Err "Bad Index"
else
case l of
[] ->
Err "Not found"
(x::xs) ->
if i == 0 then
Ok <| func x :: xs
else
Result.map ((::) x) <| changeAt xs (i - 1) func
它并不是非常漂亮,但是如果你想避免不必要地遍历列表 - 多次 - 那么你可能想要用这样的东西。
答案 1 :(得分:0)
您正在寻找the set
function for Arrays。而不是像你所描述的那样使用低效的List,这种结构更适合你的用例。
这是您正在寻找的功能的有效实现:
changeAt : Int -> (a -> a) -> Array a -> Array a
changeAt i f array =
case get i array of
Just item ->
set i (f item) array
Nothing ->
array
另请注意此实现中的data structure is the last argument。
您的问题中的链接中提到了 Array
,但此帖子中没有人明确提到此选项。