我被要求为我申请的工作编写练习的测试代码。

我被拒绝了,我也不知道为什么。我想问一下您对我的代码的看法。

练习是这样的:


用您选择的语言编写一个简单的类来表示一个
卡片组,可进行洗牌并发出一张卡片的操作。虽然今天不是必需的,但未来可能的增强是
需要处理卡组中的所有卡。尽管不是严格
,但我们重视使用说明,建模良好的数据,自动化
测试以及对体系结构决策的周全考虑,以及
简单性与完整性的权衡。

我选择用TypeScript编写代码,因为这个职位是前端开发人员。

所以,这是我的代码,您告诉我我做错了什么:

第一个文件中:card.ts

/* *** in the file Card.ts ***** */
    // Picture cards (Jack, Queen, King and Ace) will have matching numeric rank
    enum PictureCardType {Jack = 11, Queen = 12, King = 13, Ace = 1};

    enum SuiteType { Hearts , Diamonds, Spades, Clubs};


    class Card {
        private _rank: number;
        private _type: SuiteType;
        private _isPicture = function() : boolean {
           return this._rank > 10 || this._rank === 1;
        }

        constructor(rank: number, type: SuiteType) {
            if (rank > 0 && rank < 14)
                this._rank = rank;
            else
                throw new RangeError("card rank outside the range");
            this._type = type;
        }

        get rank() : number {
            return this._rank;
        }

        get suiteType() : SuiteType {
            return this._type;
        }
        // convert to JS object that has the following format: {suite: 'Dimaonds', rank : 'Ace'}
        toJSObject(): any {
            return {suite: SuiteType[this._type], rank: this._isPicture()  ?  PictureCardType[this._rank] : this._rank.toString() };
        }

    }

    export {Card , PictureCardType, SuiteType};


文件中

/* *** CardDeck.ts **** */
import {Card, SuiteType} from './card';
import {cardShuffler} from './cardShuffler';


interface cardDeckContract {
    cards : Card[];
    shuffle(): void;
    dealCard(): Card;
}

let privateCards : Card[];

// Hey, why not use JavaScript generator to deal sequence of cards?
// This might be over-architecture, but it is a way to show the usage of generators
let getNexCard =  function*() {
    while (privateCards.length > 0) {
        let card: Card = privateCards.splice(0, 1)[0];
        yield card;
    }
};
// initiallize the generator
let cardSequence : IterableIterator<Card> = getNexCard();

// Class CardDeck is responsible of implementing the contract above: Shuffle and Deal a card

class CardDeck implements cardDeckContract {

    cardShuffler: cardShuffler;

    /// CardDeck is not the class that has shuffling algorithm.
    /// We pass the shuffling algorithm to its constructor as a simple way of Dependency Injection
    /// This decision will help us chose different shuffling ways in the future.
    /// So the CardDeck is like Strategy Pattern
    constructor(cards: Card[], cardShuffler: cardShuffler) {
        privateCards = cards.slice(0);
        // this is the shuffling utility passed to the class
        this.cardShuffler = cardShuffler;
    }

    shuffle()  {
        // calling the shuffling
        let copy = this.cardShuffler.shuffle(privateCards);
        // slice(0) is a simple way to do array cloning
        privateCards = copy.slice(0);
    }

    get cards() : Card[]  {
        return privateCards;
    }

    // dealCard is implemented as iterator though the usage of Generator in JavaScript ES6
    // see the code above
    dealCard() : Card {
        return cardSequence.next().value;
    }

}

export default CardDeck;

< br并在文件cardShuffler.ts

/* CardShuffler.ts */
import {Card, SuiteType} from './card';

// cardShuffler is the contract the define shuffling array of cards

export interface cardShuffler {
    shuffle(inputCards: Card[]) : Card[];
}


// this is a simple implementation of a shuffling algorithm
export class simpleCardShuffler implements cardShuffler {
    shuffle(inputCards: Card[]) : Card[] {
        // we implement a simple shuffling algorithm
        var copy: Card[] = [], n : number = inputCards.length, i : number;
        while(n) {
            i = Math.floor(Math.random() * n--);
            copy.push(inputCards.splice(i, 1)[0]);
        }
        return copy.slice(0);
    }
}


// in the future, and for more requirements we can implement different shuffling logic here, that can be injected into the CardDeck


,最后在文件中:CardDeckFactory.ts

/* CardDeckFactory *** */
import CardDeck from './cardDeck';
import {Card, SuiteType} from './card';
import {simpleCardShuffler, cardShuffler} from './cardShuffler';

let buildStandardDeck = function() : Card[]{
    let cards = new Array<Card>();

    for (let suite in SuiteType) {
        if (!isNaN(parseInt(suite))) {
            let suiteAsNumber: number = parseInt(suite);
            for (let rank = 1; rank < 14; rank++) {
                cards.push(new Card(rank, suiteAsNumber));
            }
        }
    }
    return cards;
};

// this is the CardDeck Factory
// it is responsible of building CardDeck with the proper dependencies

export class CardDeckFactory {
    // StandardDeck is a CardDeck with the standard playing cards (52), and the simpleCardShuffler
    public static CreateStandardDeck() : CardDeck {

        return new CardDeck(buildStandardDeck(), new simpleCardShuffler());
    }
}


============================
对于单元测试:

CardDeck-test.ts

/* CardDeck-test.ts */
import { suite, test } from "mocha-typescript";
import {expect} from 'chai';
import * as mocha from 'mocha';
import * as Sinon from 'sinon';

import CardDeck from '../src/model/cardDeck';
import {Card, SuiteType}  from '../src/model/card';
import {cardShuffler, simpleCardShuffler} from '../src/model/cardShuffler';



describe ("CardDeck Tests", function() {

    let testCards: Card[] = [
        new Card(2, SuiteType.Clubs), 
        new Card(11, SuiteType.Hearts), 
        new Card(5, SuiteType.Diamonds),
        new Card(8, SuiteType.Spades),
        new Card(10, SuiteType.Diamonds)
        ];
    let proposedLength : number = testCards.length;


    @suite("When shuffle")
    class Basic {
        subject: CardDeck;
        mockCardShuffler : Sinon.SinonMock;
        before() {
            /* We create a mock for the cardShuffler, and pass the mock to the cardDeck
             If you are used to mock framework in .NET, Java, then you will find
             this mocking unusual.
             Sinon mocking is different that .NET , Java common mocking frameworks.
             As you see we create a concrete object of simpleCardShuffler.
             But even with this, we mock its method "shuffle", so when we call that method, 
             it is not simpleCardShuffler's method that is going to run, but it is our mock. */
            let shuffler = new simpleCardShuffler();
            this.mockCardShuffler = Sinon.mock(shuffler);
            // As you see here , we mock the method shuffle
            this.mockCardShuffler.expects("shuffle").returns([]);
            this.subject =  new CardDeck(testCards, shuffler);
        }


        @test("should call the shuffler passed from constructor")
        assert_shuffle_result_length() {
            // make sure that before shuffle we have all test cards (5)
            expect(this.subject.cards.length).to.equals(proposedLength);
            // Now this call the passed parameter, but as we mocked the method, then it is our mock that will be called.
            this.subject.shuffle();
            expect(this.subject.cards.length).to.equals(0)
            this.mockCardShuffler.verify();  // verify that we got the call 
            this.mockCardShuffler.restore(); // restore everything back and the class simpleCardShuffler will be back as it used to be
        }

    }

    describe ("When deal a card", function() {

        @suite("and when there are cards in the deck")
        class NextCardWithCards {

            subject: CardDeck;

            before() {
                this.subject =  new CardDeck(testCards, null);
                proposedLength = testCards.length;
            }
            @test("should get the first card from the deck") 
            assert_nextCad() {
                let card = this.subject.dealCard();
                expect(card.rank).to.equal(2);
                expect(card.suiteType).to.equal(SuiteType.Clubs);
            }


            @test("and the deck should be reduced by one card") 
            assert_cardRemoved() {
                expect(this.subject.cards.length).to.equal(proposedLength);
                let card = this.subject.dealCard();
                expect(this.subject.cards.length).to.equal(proposedLength - 1);
            }
        }

        @suite("and when there are no more cards in the deck")
        class NextCardWithoutCards {

            subject: CardDeck;

            before() {
                this.subject =  new CardDeck(testCards, null);
            }
            @test("should get undefined") 
            assert_nextCad() {
                var n = testCards.length;
                let localcard : Card;
                while(n--) {
                    localcard = this.subject.dealCard();
                    expect(localcard).to.be.not.null;
                }
                localcard = this.subject.dealCard();
                expect(localcard).to.be.undefined;
            }
        }
    });
});


和卡测试

/* Card-test.ts */

import {suite, test} from 'mocha-typescript';
import * as mocha from 'mocha';
import {Card, PictureCardType, SuiteType} from '../src/model/card';
import {expect} from 'chai';
describe("Card tests", function() {
    @suite("When create a new Card")
    class Basic {
        @test("can pass a numeric value as rank property")
        assert_rank_assignment() {
            var subject: Card = new Card(10, SuiteType.Clubs);

            expect(subject.rank).to.equal(10);
            expect(subject.suiteType).to.equal(SuiteType.Clubs);
        }   

        @test("or passing a picture type as rank property")
        assert_rank_assignment_with_picture_parameter() {
            var subject: Card = new Card(PictureCardType.Jack, SuiteType.Hearts);
            expect(subject.rank).to.equal(11);
            expect(subject.suiteType).to.equal(SuiteType.Hearts);
        }

        @test("should fail when rank is outside 1-13 range")
        assert_fail() {
            expect(() => {
                var subject: Card = new Card(14, SuiteType.Diamonds);
            }).to.throw("rank outside the range");
        }
    }

    @suite("When dealing with picture cards")
    class RankType{
        @test("should be able to switch between the Jack card and its equivalant numeric 11")
        assert_jack() {
            var subject: Card = new Card(PictureCardType.Jack, SuiteType.Diamonds);
            expect(subject.rank).to.equal(11);
            expect(subject.rank).to.equal(PictureCardType.Jack);
        }
    }


            //and dealing with a picture card"
        @suite("When convert a JavaScript Object")
        class JSObjectTest {
            obj: any;
            before() {
                var subject : Card =  new Card(PictureCardType.Jack, SuiteType.Diamonds);
                this.obj = subject.toJSObject();
            }
            @test("should convert suiteType to its string name")
            assert_suitename() {
                expect(this.obj.suite).to.equal('Diamonds');
            }
            @test("should convert rank to its picture name for picture cards")
            assert_jsonPictureCard() {
                expect(this.obj.rank).to.equal('Jack');
            }

            @test("should convert rank to its numeric value numeric cards")
            assert_jsonNumeriCard() {
                var subject : Card =  new Card(5, SuiteType.Diamonds);
                var json: any = subject.toJSObject();
                expect(json.rank).to.equal('5');
            }

        }


});


我不会继续,因为我认为这已经足够了

那你觉得呢?

我做错了什么?以及我该如何做得更好?

评论

我不知道TypeScript。我会从每个级别获得一个枚举-两个,三个...级别和西服(非套件)都从0开始。尽量使用ace。当您进行计算时,这更容易。套房的i / 13和等级的i%13使生成变得容易。有一种标准的Yates随机播放被证明是正确的并且非常有效。

尚不清楚,但是您知道您因为代码而被拒绝了吗?面试小组中的某个人甚至可能在拒绝您之前就拒绝了您。或者,尽管他们知道他们将是他们的第一选择,但您可能已被邀请作为第二选择。作为数十次技术面试的一部分,传递某人的原因是如此多样,并且(坦率地说)并不总是公平的。
纯粹出于个人观点:如果它分布在五个或更多文件中并实现了IterableIterator,则使用哪种语言都没有关系-最糟糕的是Java。从必须保持这一点的人的角度考虑它!每分钟产生多少个WTF?

“测试代码”:英文不正确。参见english.stackexchange.com/a/20456/97308。

最明显的是您拼错了“ suit”并将其设置为“ suite”。

#1 楼


尽管不是严格要求,但我们重视使用说明,建模良好的数据,自动化测试以及对体系结构决策和简单性与完整性的权衡取舍的认真考虑。给定此子句,您的代码中会有一些突出的地方:


TypeScript-TypeScript对于此问题过于矫kill过正。 TypeScript是一种很好的语言,但并非所有项目都需要TypeScript甚至可以以明智的方式集成TypeScript。例如,如果您陷入一个只需要一点点jQuery的WordPress项目中,您真的需要TypeScript吗?您的语言选择还反映了您为项目选择技术的方式。仅仅因为您在TypeScript方面表现出色,并不意味着其他任何人都可以做到。考虑学习曲线,斜坡时间,维护。今天的代码是明天别人的问题。
类,接口,模式-也会过分杀人。您的牌组可以只是suitvalue的对象数组。您的操作可能只是接受输入和返回输出的函数。新功能只是意味着新功能。没有花哨的图案,工厂,界面和所有闪亮的东西。
您选择的单元测试设置-使用类时,您倾向于处于隐藏状态,这需要访问器,从而导致测试前设置过于复杂。正确设置对象状态,然后选择膨胀的测试运行程序(*咳嗽*茉莉*咳嗽*)和样板测试规范(*咳嗽*硒*咳嗽*)。如果您只是使用简单的开放数据结构,并在可能的情况下避免发生突变,则无需隐藏属性或使用副作用函数,从而使所有事情都只需深入比较预期数据结构和实际数据结构即可。 >缺少文档-代码注释很好,但是有一种结构化的方法可以记录JS。参见JSDoc。在大多数情况下,您只需要知道如何写注释(以/**开头),@param@return即可。
复杂性-只是说要使用类的说明并不意味着您应该这样做。在现实世界中,您需要技巧来挑战说明,以寻求更简单的解决方案,能够回击不合理的要求并准时交货。许多项目由于过度设计的代码而无法按时交付,最终导致大量的技术债务或开发开销或维护麻烦。具有3个功能和一个简单的QUnit测试。由于TypeScript只是ES的一个超集,因此以下内容完全有效: >
const values = [1,2,3,4,5,6,7,8,9,10,11,12,13];
const suits = ['HEARTS', 'CLUBS', 'SPADES', 'DIAMONDS'];

/**
 * Builds a deck, an array of { suite, value }.
 * @return Array The new deck.
 */
export function createDeck(){
  const values.reduce((c, v) => ([ ...c, ...suits.map(s => ({ value: v, suit: s })) ]), []);
}

/**
 * Shuffles a deck.
 * @param Array deck The deck to shuffle.
 * @return Array The shuffled deck.
 */
export function shuffleDeck(deck){
  // Shuffle logic. Make sure you aren't mutating deck.
  return shuffledDeck;
}

/**
 * Chooses a card from the top of the deck
 * @param Array deck The deck to draw from.
 * @return Object An object that contains the next card and the changed deck
 */
export function dealOneCard(deck){
  if(!deck.length) throw new Error('Deck is empty');
  return { card: deck[0], deck: deck.slice(1) };
}



虽然今天不是必需的,但未来可能的增强是需要将所有卡片都放在一个牌组中。顺便说一下发行所有卡只是在退回的卡牌变空之前发行一张卡。这更像是业务需求,而不是编码需求。可以将其视为某些纸牌游戏而非所有纸牌游戏所需的东西。让此API的使用者处理该问题。

评论


\ $ \ begingroup \ $
评论不用于扩展讨论;此对话已移至聊天。
\ $ \ endgroup \ $
– Mathieu Guindon♦
17年4月13日在20:33

\ $ \ begingroup \ $
诚实的问题,您的第一个对等点(使用Typescript)是否无关紧要,因为仅要求OP开发业务逻辑,而没有关于将在何处实现的参考框架? OP选择了一种前端语言,因为它是一种前端位置,他本可以选择一种更简单的前端语言。但是面试官不知道他为什么选择打字稿。问题定义列出了有价值的标准,并且语言选择未在此处列出,因此它与面试官似乎无关紧要(在一定程度上,例如Malbolge会很愚蠢)
\ $ \ endgroup \ $
–更
17-10-19在13:39



#2 楼

除了审查过您公司代码的人之外,谁都不能肯定地说出您为什么被拒绝。

这还取决于您所申请的职位。如果您要申请入门级或高级职位,期望会大不相同。

作为招聘经理,我遇到了很多事情(从与我最不相关到与我最不相关,一个潜在的招聘经理):



您没有按照指示进行操作。


虽然今天不是必需的,但可能将来的增强是需要将所有纸牌都放在一副纸牌中。 />这是有争议的。一些招聘经理会认为,“嘿,他超越了。”其他人会认为:“我只是问过您要建造甲板并对其进行洗牌。这个家伙会不断尝试并即时调整要求吗?”

我看到双方,但会错请按照指示进行操作。可能包括一些注释,描述您将如何处理卡片,并提及您认为添加卡片相当琐碎,因此您应该与主管/经理讨论从一开始就添加卡片的问题。或在您未遵循规范之前提出要求的评论中实施它,但假设经理会同意,那么您就可以构建它。

您的随机播放测试实际上并不能确认甲板被打乱,只是它仍然以相同的大小存在。我希望看到卡座的顺序不同,并且当我再次洗净原始卡座时,我得到了一个新的三阶。我希望UT确保混洗至少是伪随机的。
我首先注意到的是Cards.ts中的第二行代码。卡没有套间,有套间。一些招聘经理可能会立即停止寻找。拼写错误的变量是维护的噩梦。

您的某些评论中有拼写错误,单词遗漏或使用了错误的单词(“帮助我们选择”应该是“帮助我们选择”)

另一种选择,但这是一次求职面试。这是您最好的前进方向。如果您在面试阶段想快速找到细节,而一旦我们雇用您,您为什么不犯更多错误呢?

第一次使用.slice(0)时,您没有评论,但是第二次,您愿意。我怀疑您首先写了第二个实例以及注释,但是第一个实例应该有注释(如果不是每个实例)。

我喜欢CardDeckFactory可以扩展为不仅仅是硬编码的标准52卡座,但可以与Pincochle,Eucher或其他非标准卡座配合使用。

我喜欢您有能力使用不同的随机播放算法。通常是干净,清晰且易于遵循的。

我喜欢您的许多评论,尤其是更长,更具描述性的评论。作为不熟悉TypeScript的人,我感谢单元测试中有关模拟的评论。我也喜欢您关于使用Generator的评论。我以前从没见过,所以它向我表明您有相当不错的理解深度。

其他人可能会有更多技术评论。


值得的是,如果人力资源部将问题和答案交给我,我会竖起大拇指接受后续采访。在该后续文章中,我将针对问题#1提出一些问题,以更好地理解您为什么不符合规范。


编辑添加:将解决不同的问题。 Joseph The Dreamer的回答很可能是正确的,因为您太复杂了(尽管我认为他的回答可能太简单了,以至于再次忽略了方向)。

要牢记一件好事,尤其是在电话采访之前,审查此事的人可能每天都会审查数十条。保持简短,甜美并切入重点可能是一件好事。可能有些人看到几百行代码,并会立即拒绝您。

评论


\ $ \ begingroup \ $
我认为不同洗牌算法的功能实际上是不利的。有几种不同的算法可以正确地对牌进行改组,但是只有一种数学标准可以判断它们是否正确。包括一种数学上正确的混洗算法并完成。
\ $ \ endgroup \ $
–李·丹尼尔·克罗克(Lee Daniel Crocker)
17年4月11日19:50

\ $ \ begingroup \ $
“如果您要快速处理并丢失详细信息”,我将认为这是故意的...
\ $ \ endgroup \ $
– Jeffrey Bosboom
17年4月11日在21:51

\ $ \ begingroup \ $
@JeffreyBosboom是...故意...:D oops
\ $ \ endgroup \ $
–克里斯G
17年4月11日在22:25

\ $ \ begingroup \ $
作为一名程序员,我自己:“尽管我认为他的回答可能太简单了”->使事情保持简单本身就是一项技能。我已经构建了超级通用的解决方案,但是我得到的更好的是,我编写了代码的simpeler。我准备了代码(这很重要),以便可以轻松地对其进行扩展,但实际上并没有做到。节省了很多宝贵的时间。这就是为什么我更喜欢约瑟夫的代码。一眼就可以理解。
\ $ \ endgroup \ $
–马丁(Martijn)
17年4月12日在8:03



\ $ \ begingroup \ $
@Martijn唯一的问题是它无视面试官的要求,即您“编写简单的课程”。在面试问题的上下文中,您可以在评论中描述您的思维过程,甚至可以使用类提供简单的解决方案和简单的解决方案。
\ $ \ endgroup \ $
–克里斯G
17年4月12日在15:08

#3 楼

这主要是我要解决的方式(在Python中):而deal_all是两个衬里:while self._cards: yield self.deal()

卡图片之类的东西与核心逻辑无关,您也不必始终为每个卡座中的每个Card保留它们。

评论


\ $ \ begingroup \ $
为什么用python代替打字稿?
\ $ \ endgroup \ $
–斯蒂芬·劳赫(Stephen Rauch)
17年4月11日在17:42

\ $ \ begingroup \ $
因为我了解Python,并且知道如何轻松地解决此特定问题。答案在其他语言中应该不会有太大区别。
\ $ \ endgroup \ $
–扎
17年4月11日在17:45

\ $ \ begingroup \ $
如果我正在评估应聘者提交的这份材料,那么我会因为没有实施诉讼和等级评定而被认为不完整。
\ $ \ endgroup \ $
– 200_success
17年4月11日在18:08

\ $ \ begingroup \ $
@ 200_success既不是问题,也不是仅凭声明就如何最好地做到这一点。显然很清楚的是,代表该问题的单张卡片的方式(同样,如果您要实现西装,等级和图片等),则是整数索引(例如list(itertools.product('CSHD', ['A',* range(1,11),*'JQK']))[index]),而不是微服务。
\ $ \ endgroup \ $
–扎
17年4月11日在18:23

\ $ \ begingroup \ $
西服和军衔在这里很简单:(n&0x03),(n >> 2)。如果需要,可以使它们起作用,但是内联它们可能会更快。同样,以这种方式定义,仅将数字与<和>进行比较就可以比较等级,而不必将其分开。
\ $ \ endgroup \ $
–李·丹尼尔·克罗克(Lee Daniel Crocker)
17年4月11日在20:00

#4 楼

我很同情:)
我不确定我的回答是否会真正有用,因为它要么是繁琐的工作,要么是一系列艰苦的工作,但希望它能对我有所帮助。有缺陷的实验

当我们在面试中对某人进行测试时,我们要测试什么?
如果是“这个人是一个好的程序员”,我们正在测试大量的候选者,那么我们正在测试一个假设,这是一个实验。

将质量控制应用于实验本身,面试将如下所示:


从一个大的池中随机选择测试问题,因为所有候选人
对候选人的观察是匿名的(即提交或其他内容)
结果的评估也可以匿名进行
当所有候选人都经过测试和评估后,测试结果将与他们的申请相关联
所有这些都在评估者或招聘经理不知道谁在做什么的情况下完成
我认为这只是一次(或可能是两次)盲目实验-我不知道,我不是科学家,所以我不进行采访中的实验!

对您来说,最重要的部分是应该随机选择问题,而访调员不应该对此有所了解。与您面谈的人已经从内部人员和以前的候选人那里获得了对该问题的预先了解。这使他们成为问题的领域专家,并且像大多数专家一样,这将加剧他们对你们之间任何知识或技能差距的认识。从看到别人比自己更好地解决它开始,他们就对它具有自卑感,并且在复制它们时会倾向于惩罚自己的错误。而且,和!面试官实际上可能是好人,对他们的所作所为感到难过,但会陷入困境!也许还有一些疯狂的怪异公司文化。您不想在那里工作broski :)

/ rant

没有汤匙

您的主要特征(没问题)解决方案是您一直在考虑卡片的物理现实。如果您是应用程序开发人员,那么这是一件好事,因为您将以客户理解的方式进行开发。

如果您真的有兴趣,成为抽象开发人员只是要做的事情。它也是一个强大的技能组。我绝对不会将其称为职业道路。

很难掌握抽象表示,但是您可以通过练习函数式编程(例如SICP等免费教程书)和离散数学来快速跟踪它。

在软件开发中,您可以练习按合同设计,实体关系建模,数据库规范化和UML之类的事情。所有这些实践的约束都极大地推动了对抽象和问题组合质量的认识。上面的技术都不是顺序的,这就是为什么它们迫使我们认识抽象的原因-我们可以在实践中实践更多的技术。很难理解他们想让您知道的事情。)

这里是对您的问题的实用分析:两个有限集的乘积
随机洗牌是一种新状态,它是对有限集的排序。解决方案中需要什么元素?


有限集
可以排序和变异的有限集

听起来像数组!

关于操作呢?


生成笛卡尔产品
随机订购
pop

好了,数组已经有了pop,所以我们实际上只需要两个函数(显然要在Google中使用)

现在我们有了功能性的方面,我们可以考虑使用非功能性的功能:


我们的两组(秩和西服)可以在代码中表示它们的域(但请看成我们的集实现是一个数组,它们仍然需要是数组)
每个排列实际上是一张卡片,因此应表示为
排列是计算的结果,而不是状态,因此应该是不变的(即card是一个不可变的类)
数组的定义本质上是一个有限有序的可变集合,因此它符合我们的问题,我们应该将其保留为原样

最后,必须测试笛卡尔乘积和随机排序。由于这些都不是专门针对卡的,因此我们可以使用较小的集进行测试,并且我们还演示了可重复使用的代码。这很好,因为我很懒,并且我讨厌编写测试和调试程序:) )

评论


\ $ \ begingroup \ $
听到听到!我曾在一家有史以来见过最长的最奇怪的编码测试的公司工作。解决方案,那么您可能会得到最高分,如果您尝试过并且让它更糟,那么您可能会失去价值。测试。我为什么要离开?一个女孩来的分数为-1500,在一张纸上哭泣,他们雇用了她,给了她比我更多的钱
\ $ \ endgroup \ $
–BugFinder
17年4月13日在11:26

\ $ \ begingroup \ $
@Seth:“双盲”只是意味着研究人员不知道对照组中的谁。 (显然,受试者必须永远不知道这一点)。我想拥有一个控制组的一种方法是让现有团队也提交答案,然后将它们扔到同一个匿名锅中……但这并不是完全的双盲,那么:只有审阅者不知道哪个代码来自对照组。
\ $ \ endgroup \ $
– jpaugh
17年4月13日在17:53



#5 楼

明显的错误是您使用了类和私有全局变量的奇怪组合。有人同时使用两个CardDeck对象时会发生什么?使其难以阅读。真的,这很简单,就像

class CardDeck {
    cards: String[];

    constructor(cards: String[]) {
        this.cards = cards.slice(0);
    }

    shuffle()  {
        // Fisher-Yates
        for (let i = this.cards.length - 1; i--; i > 0) {
            let j = Math.floor(Math.random() * (i + 1));
            [this.cards[i], this.cards[j]] = [this.cards[j], this.cards[i]];
        }
    }

    deal(): String {
        return cards.pop();
    }
}


评论


\ $ \ begingroup \ $
同意。有趣的是,我从与您编写的内容相似的内容开始,但是我认为我无法提交对代码练习简单的内容。因此,我添加了一些好的程序员应该具备的“风味”。
\ $ \ endgroup \ $
–山姆·史密斯
17年4月17日在13:50

#6 楼

我没有做详细的分析,但是我扫描了代码。总的来说,我认为它确实不错-尽管我不熟悉所讨论的语言。


首先,我同意其他评论者的意见,认为这对于一个非常简单的事情而言似乎相当复杂且过度设计。特别是在摘要中提到“简单性-完整性”时。您的语言肯定存在吗?未能将现有库用于类似的事情是一个很大的危险信号。我在一次电话采访中进行了30分钟的交谈,一次是关于数据结构,效率以及各种方法的优缺点的,这是由于他随便问了一下代表一副纸牌的问题,我们讨论了完全构建它的不同方式。这一切都是因为当他问我要如何表示时,我说“那将取决于它的用途”,并继续解释其重要性。

评论


\ $ \ begingroup \ $
不幸的是,没有与雇主交谈。这是通过招聘人员发生的,而我只是得到了被拒绝的结果。
\ $ \ endgroup \ $
–山姆·史密斯
17年4月11日在20:49



\ $ \ begingroup \ $
@SamSmith您仍然可以在响应中建立一些对话。换句话说,在“我假设需求为X。请注意在其他情况下,例如Y,我改为Z”。
\ $ \ endgroup \ $
– Tim B
17年4月11日在20:51

\ $ \ begingroup \ $
是的,我下次会写更多评论,现在完成
\ $ \ endgroup \ $
–山姆·史密斯
17年4月11日在21:16

\ $ \ begingroup \ $
@SamSmith在大多数情况下,您的评论都很好。您确实有一个毫无意义的评论(称为混洗),但除此之外,它还不错。我不建议过于沉迷。只是一个介绍性的注释,表明您了解可以使用多种方法来构建此结构,并解释了为什么选择所做的方法。例如,您在纸牌甲板顶部说明了洗牌的策略模式,这很好。
\ $ \ endgroup \ $
– Tim B
17年4月12日在8:13

\ $ \ begingroup \ $
等等...能给我列出内置洗牌机的语言吗?我可以想到零,也许是一。
\ $ \ endgroup \ $
–茉莉
17年4月13日在20:01

#7 楼

我不知道TypeScript,但我是扑克玩家。
您应该使用两个-ace来排名
拥有高的ace,因为它将以这种方式用于计算
可以轻松地从整数
实现卡比较
不确定是否是有效的随机播放
没有卡就怎么办
这是C#版本



评论


\ $ \ begingroup \ $
对不起,任何包含5 = 3的代码对我而言都将立即导致可读性/维护性问题。如果要使用枚举,请给它们使用非冲突名称。 Deuce和Jack可能还可以,但是数字应该是FiveSpot或Rank5。
\ $ \ endgroup \ $
–李·丹尼尔·克罗克(Lee Daniel Crocker)
17年4月11日在20:27

\ $ \ begingroup \ $
@LeeDanielCrocker您知道您可以将枚举转换回一个字节吗?从0开始保存位。如果不是从0开始,则返回(RankSam)(quickCard%13);将无法正常工作。使用数字表示卡中的数字似乎很标准。
\ $ \ endgroup \ $
–狗仔队
17年4月11日在20:42



\ $ \ begingroup \ $
是的,我也不会那样做。除以13很昂贵。将西装放在低位,等级放在高位,所以等级现在是(n >> 2),而西装是(n&0x03)。这也使您可以比较等级(最常见的操作),而不必仅通过比较数字就将其完全分开。
\ $ \ endgroup \ $
–李·丹尼尔·克罗克(Lee Daniel Crocker)
17年4月11日在20:45

\ $ \ begingroup \ $
@LeeDanielCrocker我认为在这里捣碎会有些糊涂。感谢您的输入。
\ $ \ endgroup \ $
–狗仔队
17年4月11日在20:49

\ $ \ begingroup \ $
最后,这种洗牌不适合费舍尔·耶茨。第一个随机数必须是0..51,然后是0..50,然后是0..49,依此类推。您从0..50开始。据我所知。剩下的底卡就位!
\ $ \ endgroup \ $
–李·丹尼尔·克罗克(Lee Daniel Crocker)
17年4月11日在20:49

#8 楼

如果我是审稿人,我将在这一行停下: ,该列表将更改。所以你不能有多个套牌?这是设计中的主要缺陷。

对我来说,这是一个危险的信号。其余代码实际上非常好。我喜欢TypeScript,并且您很好地使用了面向接口和面向对象的设计。很遗憾,您看不到在代码中使用全局变量是多么的错误。

并使用短绒。我将您的代码放入tslint中,发现了49个掉毛错误,这是巨大的!