Linq-获取数组中的连续数字
本文关键字:连续 数字 获取 数组 Linq- | 更新日期: 2023-09-27 18:20:10
我正在创建一个扑克系统,目前正在精简我的手计算器。
以下代码有效:
public enum CARDS
{
None = 0,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
Ace
};
public enum SUITS
{
None = 0,
Diamonds,
Clubs,
Hearts,
Spades
};
public class Card
{
public CARDS Val { get; set; }
public SUITS Suit { get; set; }
}
public class IntIndex
{
public int Count { get; set; }
public int Index { get; set; }
}
static void Test()
{
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Four });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
// I have a processor that iterates through the above card list and creates
// the following array based on the Card.Val as an index
int[] list = new int[] {0,0,0,1,1,2,1,1,0,0,1,0,0,0};
List<IntIndex> indexList =
list.Select((item, index) => new IntIndex { Count = item, Index = index })
.Where(c => c.Count > 0).ToList();
List<int> newList = (from i in indexList
join j in indexList on i.Index equals j.Index + 1
where j.Count > 0
select i.Index).ToList();
// Add the previous index since the join only works on n+1
// Note - Is there a way to include the first comparison card?
newList.Insert(0, newList[0] - 1);
// Nice! - got my straight card list
List<CARDS> cards = (from l in newList
select (CARDS)l).ToList();
}
然而,我想让它更紧凑,如:
static void Test()
{
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Four });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
List<Card> newList1 = (from i in cardList
join j in cardList on i.Val equals j.Val + 1
select i).ToList();
// Add the previous index since the join only works on n+1
// Similar to: newList1.Insert(0, newList1[0] - 1);
// However, newList1 deals with Card objects so I need
// To figure how to get the previous, non-duplicate card
// from the original cardList (unless there is a way to return the
// missing card!)
}
问题是Six正在重复。Distinct和自定义比较函数不起作用,因为这将破坏n+1联接子句。
另一个问题是以下卡片列表:
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Three });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Jack });
我得到了一个3 Hearts、6 Diamond、7 Hearts和8 Hearts的返回列表,因为2和3是连续的。
我真正想要的是一个列表,它返回5张或更多的连续牌,或者更好的是,连续序列的前5张牌。因此,由于输入列表中没有5张连续的卡,因此上面的列表将返回空。
如果您有兴趣从cardList
中获得具有最高顺序卡值范围的卡的子集,无论是否适合,请考虑这种方法。
//Extension method to find a subset of sequential consecutive elements with at least the specified count of members.
//Comparisions are based on the field value in the selector.
//Quick implementation for purposes of the example...
//Ignores error and bounds checking for purposes of example.
//Also assumes we are searching for descending consecutive sequential values.
public static IEnumerable<T> FindConsecutiveSequence<T>(this IEnumerable<T> sequence, Func<T, int> selector, int count)
{
int start = 0;
int end = 1;
T prevElement = sequence.First();
foreach (T element in sequence.Skip(1))
{
if (selector(element) + 1 == selector(prevElement))
{
end++;
if (end - start == count)
{
return sequence.Skip(start).Take(count);
}
}
else
{
start = end;
end++;
}
prevElement = element;
}
return sequence.Take(0);
}
//Compares cards based on value alone, not suit.
//Again, ignores validation for purposes of quick example.
public class CardValueComparer : IEqualityComparer<Card>
{
public bool Equals(Card x, Card y)
{
return x.Val == y.Val ? true : false;
}
public int GetHashCode(Card c)
{
return c.Val.GetHashCode();
}
}
考虑到以上情况,方法是首先根据卡片的价值而不是套装对卡片进行排序,然后按降序排列。然后创建一个不同卡片的子集,同样只基于卡片的价值,而不是适合。然后调用FindConsecutiveSequence
,指定用于比较的Val属性以及有效序列所需的元素数量。
//Sort in descending order based on value of the card.
cardList.Sort((x,y) => y.Val.CompareTo(x.Val));
//Create a subset of distinct card values.
var distinctCardSet = cardList.Distinct(new CardValueComparer());
//Create a subset of consecutive sequential cards based on value, with a minimum of 5 cards.
var sequentialCardSet = distinctCardSet.FindConsecutiveSequence(p => Convert.ToInt32(p.Val), 5);
我认为这应该涵盖你在问题中提出的问题,并给你一些可以建立的东西。然而,如果这是针对扑克的,那么在Ace可以是一个低值->{a,2,3,4,5}的情况下,这种逻辑将失败。我没有看到需要提到Ace特定的逻辑,所以也许你在问题的范围之外处理它。
按编号订购卡片,然后取1张并跳过其中的3张以选择您想要的卡片。
这就是您想要的吗?
首先是套装的随机列表,但卡片值的顺序列表
Random random = new Random((int)(DateTime.Now.ToBinary() % Int32.MaxValue));
List<Card> hand = new List<Card>();
for(int card = (int)CARDS.Five;card <= (int)CARDS.Nine;card++)
{
SUIT suit = (SUITS)(random.Next(4)+1);
hand.Add(new Card { Suit = suit, Val = (CARDS)card });
}
或者一个顺序的西装清单是…
for(int card = (int)CARDS.Five, int suit = (int)SUITS.Diamonds;card <= (int)CARDS.Nine;card++, suit++)
{
if(suit > (int)SUITS.Spades)
suit = (int)SUITS.Diamonds;
hand.Add(new Card { Suit = (SUITS)suit, Val = (CARDS)card });
}
使用Aggregate方法。您可以修改下面的示例代码以返回不同长度的卡片列表,并更改while子句以检查必须匹配的卡片数量。(例如,我的版本检查计数==5,你可以检查计数>=5,等等)
public class Card {
public CARDS Val { get; set; }
public SUITS Suit { get; set; }
// added ToString for program below
public override string ToString() {
return string.Format("{0} of {1}", Val, Suit);
}
}
class Program {
static IEnumerable<Card> RandomList(int size) {
var r = new Random((int)DateTime.Now.Ticks);
var list = new List<Card>();
for (int i = 0; i < size; i++) {
list.Add(new Card {
Suit = (SUITS)r.Next((int)SUITS.Diamonds, (int)SUITS.Spades),
Val = (CARDS)r.Next((int)CARDS.Two, (int)CARDS.Ace)
});
}
return list.OrderBy(c => c.Val);
}
// generates a random list of 5 cards untill
// the are in sequence, and then prints the
// sequence
static void Main(string[] args) {
IEnumerable<Card> consecutive = null;
do {
// generate random list
var hand = RandomList(5);
// Aggreate:
// the passed in function is run for each item
// in hand. acc is the accumulator value.
// It is passed in to each call. The new List<Card>()
// parameter is the initial value of acc when the lambda
// is called on the first item in the list
// in the lambda we are checking to see if the last
// card in the accumulator value is one less
// than the current card. If so, add it to the
// accumulator, otherwise do not.
consecutive = hand.Aggregate(new List<Card>(), (acc, card) => {
var size = acc.Count != 0
? ((int)card.Val) - ((int)acc[acc.Count - 1].Val)
: 1;
if (size == 1)
acc.Add(card);
return acc;
});
} while (consecutive.Count() != 5);
foreach (var card in consecutive) {
Console.WriteLine(card);
}
Console.ReadLine();
}
}
当提供七张牌(包括A-5的边缘情况)时,以下方法应该会获得最好的直接效果,但我还没有彻底测试过。
关键是,如果你按降序对卡片进行排序,并删除任何重复的值,那么只有几种可能的方法可以排列直线(你只需要检查末端):
- 如果第一张和第五张牌相隔四张,它们代表最高的直线(因为我们知道它们之间的牌没有重复值)
- 按顺序,第二张和第六张牌以及第三张和第七张牌也是如此(如果还有那么多唯一值的话)
- 唯一的另一种可能性是,如果我们在排序列表的开头有一张王牌,在结尾有五到二张牌,代表a-5直落
这是代码:
public static IEnumerable<Card> GetBestStraight(IEnumerable<Card> sevenCards)
{
if (sevenCards.Count() != 7)
{
throw new ArgumentException("Wrong number of cards", "sevenCards");
}
List<Card> ordered = sevenCards.OrderByDescending(c => c.Val).ToList();
List<Card> orderedAndUnique = ordered.Where((c, i) => i == 0 || ordered[i].Val != ordered[i - 1].Val).ToList();
if (orderedAndUnique.Count < 5)
{
// not enough distinct cards for a straight
return Enumerable.Empty<Card>();
}
if (orderedAndUnique[0].Val == orderedAndUnique[4].Val + 4)
{
// first five cards are a straight
return orderedAndUnique.Take(5);
}
else if (5 < orderedAndUnique.Count && orderedAndUnique[1].Val == orderedAndUnique[5].Val + 4)
{
// next five cards are a straight
return orderedAndUnique.Skip(1).Take(5);
}
else if (6 < orderedAndUnique.Count && orderedAndUnique[2].Val == orderedAndUnique[6].Val + 4)
{
// last five cards are a straight
return orderedAndUnique.Skip(2).Take(5);
}
// if there's an A-5 straight, the above won't have found it (because Ace and Two are not consecutive in the enum)
if (orderedAndUnique[0].Val == CARDS.Ace && orderedAndUnique[orderedAndUnique.Count - 4].Val == CARDS.Five)
{
return orderedAndUnique.Where(c => c.Val == CARDS.Ace || c.Val <= CARDS.Five);
}
return Enumerable.Empty<Card>();
}
愚蠢和津贴一样!当最快的灵魂是一个简单的小面具:时,我为什么这么担心使用LINQ
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = RANK.Three });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = RANK.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Seven });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = RANK.Four });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = RANK.King });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Ace });
int card = 0;
foreach (Card c in cardList)
{
card |= 1 << (int)c.Val - 1;
}
bool isStraight = false;
RANK high = RANK.Five;
int mask = 0x1F;
while (mask < card)
{
++high;
if ((mask & card) == mask)
{
isStraight = true;
}
else if (isStraight)
{
--high;
break;
}
mask <<= 1;
}
// Check for Ace low
if ((!isStraight) && ((0x100F & card) == 0x100F))
{
isStraight = true;
high = RANK.Five;
}
return card;