如何在Dart中使用Future管理列表?

3

我刚接触 Dart;然后,我创建了一个名为Deck的扑克牌对象列表。我想选择一张随机的牌,然后从牌库中删除该牌。但是,由于在缩小牌库之前可能会先选择后续的牌,导致我得到了重复的牌。如果我想从牌库中选出10张独特的随机牌,我应该如何处理?

class Card{
String face;
String suit;
String rank;
String imgSrc;
String holdImgSrc;

Card(
 this.face,
 this.suit,
 this.rank,
 this.imgSrc,
 this.holdImgSrc
 );
}
import 'dart:math' show Random;
Random indexGen = new Random();


getCard1(){
  card1 = deck[indexGen.nextInt(deck.length)];
  deck.removeWhere((item) => item == card1);
  return card1;  
}         

getCard2(){
  card2 = deck[indexGen.nextInt(deck.length)];
  deck.removeWhere((item) => item == card2);
  return card2;
}

当我尝试将Card对象作为future返回时,我会得到以下错误提示:
new Future((getCard1()))
  .then((getCard2()))
  .then((getCard3()));

类型'Card'不是'计算'的类型为'() => dynamic'的子类型。

当我尝试返回牌组列表时,我得到:

类型'List'不是'计算'的类型为'() => dynamic'的子类型。

我是否遗漏了正确的语法、逻辑上的错误或者需要以不同的方式处理列表,例如观察变化?

编辑后添加:Futures语法可行,但删除似乎没有正确地进行。我更改了代码,使用Jim-Y下面建议的代码,除了使用第二个命名构造函数从列表中预加载新的卡对象。改进后的代码和输出如下:

fullDeck[
...
var tenC  = new Card.full(17,'10_of_clubs','c','10','10_of_clubs.png','10_of_clubs_h.png');
var tenD  = new Card.full(18,'10_of_diamonds','d','10','10_of_diamonds.png','10_of_diamonds_h.png');
var tenS  = new Card.full(19,'10_of_spades','s','10','10_of_spades.png','10_of_spades_h.png');
var tenH  = new Card.full(20,'10_of_clubs','c','10','10_of_clubs.png','10_of_clubs_h.png');
...]

Deck<Card> deck = new Deck<Card>();
  Random indexGen = new Random();

   for(var c = 0; c < 20; ++c) {
     var card = new Card(c);
     deck.add(fullDeck[c]);//List of 52 full card objects

           }

   for(var i = 0; i < 10; ++i) {
     var rnd = indexGen.nextInt(deck.size());
     print('${deck.get(rnd).face} Deck size: ${deck.size()}');           
           }

         }

4_of_clubs Deck size: 19
10_of_diamonds Deck size: 18
5_of_clubs Deck size: 17
4_of_spades Deck size: 16
5_of_spades Deck size: 15
10_of_clubs Deck size: 14
10_of_clubs Deck size: 13
3_of_spades Deck size: 12
5_of_diamonds Deck size: 11
3_of_diamonds Deck size: 10

正如您所看到的,十个梅花牌被打印了两次。因此,如果在第6步中删除了10,则为什么在第7步中仍然存在?


你在 getCardX() 使用的 card1card2 变量是什么? - Günter Zöchbauer
它们是卡片对象。列表deck中填充了卡片对象。 - user2550943
你正在直接调用getCard1,并将返回的Card对象作为参数传递给new Future()。为了正确的类型,你需要编写new Future(getCard1).then((_)=>getCard2()).then((_)=>getCard3())。它仍然不会记住已经移除的卡片。 - lrn
Irn:你说得对,需要记住这些卡片。它们被声明为全局变量,但没有更新。还有更多的学习曲线 :) - user2550943
2个回答

3

如果您希望以这种方式链接调用,则方法必须返回一个future:

注意:我尚未测试过代码:

// I don't see what type 'Card' actually is from your code
Future<Card> getCard1(){
  return new Future(() {
    card1 = deck[indexGen.nextInt(deck.length)];
    deck.removeWhere((item) => item == card1);
    return card1;  
  });
}  

getCard2()的情况也是一样的。

Future<Card> getCard2(){
  return new Future(() {
    card2 = deck[indexGen.nextInt(deck.length)];
    deck.removeWhere((item) => item == card2);
    return card2;
  });
}

你需要通过以下方式进行调用

getCard1().then((c) => getCard2()).then((c) => print(c));

由于getCard1getCard2方法本质上是相同的,因此您可以将它们合并为一个方法。

List<Card> cards = [];

Future<Card> getCard(int i){
  return new Future(() {
    cards[i] = deck[indexGen.nextInt(deck.length)]; // not clear what card is 
    deck.removeWhere((item) => item == card[i]);
    return card[i];  
  });
}  

.

getCard(1).then((c) => getCard(2)).then((c) => print(c));

感谢您的超快速回复和澄清。我添加了类声明,并修改了方法,终于成功摆脱了错误信息! - user2550943
好的。在组合方法上,我尝试了:deck.removeWhere((item) => item == cards[i]); return cards[i]; 并按照您的建议进行了调用。现在我得到了“null对象没有'then'方法”的错误提示。 - user2550943
1
请记得从 getCard 函数中返回 future。 - lrn
此外,您可以使用 removeAt 来移除卡片:cards[i] = deck.removeAt(indexGen.nextInt(deck.length));。当您已经知道索引时,比使用 removeWhere 更简单、更高效。 - lrn
抱歉,我正在返回从牌堆中抽取的“卡牌对象”。无论是card1还是card[i]。这现在被称为“未来”吗? - user2550943
显示剩余3条评论

1
我现在看不出你为什么需要使用Futures。在下面的代码中,我将尝试介绍一种可能更好的方法来使用Dart的通用功能从牌堆中移除一张牌 :)
你原来的Card类,我扩展了它以进行演示:
class Card {
  int id;
  String face;
  String suit;
  String rank;
  String imgSrc;
  String holdImgSrc;

  Card(this.id);
  Card.full(this.id, this.face, this.suit, this.rank, this.imgSrc, this.holdImgSrc);
}

然后,您可以为卡片创建一个通用的容器,而不是使用简单的列表。
class Deck<T extends Card> {
  List<T> _container = new List<T>();

  T get(int index) => _container.removeAt(index);
  void add(T item) => _container.add(item);
  int size() => _container.length;
}

这样做会使您的示例更易于以后扩展,并且可以获得更多的表达能力。
然后,您可以编写以下内容,从牌堆中删除10个随机元素。
void main() {
  Deck<Card> deck = new Deck<Card>();
  Random indexGen = new Random();

  for(var c = 0; c < 20; ++c) {
    var card = new Card(c);
    deck.add(card);
  }

  for(var i = 0; i < 10; ++i) {
    var rnd = indexGen.nextInt(deck.size());
    print('${deck.get(rnd).id} Deck size: ${deck.size()}');
  }
}

在这个简单的例子中,使用这些简单的卡片对象没有重复项。但是,如果需要,可以通过添加一个名为fGet的方法来扩展您的Deck类,该方法可以像@Günter之前提到的那样返回一个Future对象。
希望我给您提供了一些好的想法 :)
干杯

我同意未来似乎是不必要的。为了避免重复抽到同一张牌,我建议最简单且最符合领域的方法是洗牌后取前10张牌。也就是说,生成一副随机排列的牌组。有一个简单的算法可以实现这个过程(Knuth shuffle)。 - Alan Knight
Alan。对于洗牌,你的解决方案是最实用的。我曾试图模拟真正的牌堆,一次移除一张牌,这导致我质疑我的算法为什么会在有重复时失败,所以这个问题是一个学术性的问题,如何用这种方式做到这一点。上面的Set方法也创建了重复。谢谢。 - user2550943
T get(int index) => _container.removeAt(index); 关键在于不重复生成相同的数字,但这并不是您所期望的方式。 removeAt 不仅会返回一个项目,而且它还将从容器中删除该项目,并且Dart将重新排列(向左移动索引)元素。因此,在下一轮中,随机数生成器将创建一个介于[0-19)之间的随机数。有许多可能性不从列表中删除卡片,只需使其在下一轮中排他即可。 - Attila Kling
谢谢提醒。在原始代码中,我认为重复的数字是由于异步操作比删除操作更快导致的。经过进一步检查,似乎索引在删除后没有按照预期更新。 - user2550943
10_of_clubs 10_of_diamonds 10_of_spades 2_of_clubs 2_of_diamonds是一个列表的原始顺序。然后进行删除操作后,该列表变为:10_of_clubs 10_of_diamonds 10_of_clubs 10_of_spades 2_of_clubs。 - user2550943
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接