Quá nhiều câu lệnh 'nếu'?


263

Đoạn mã sau hoạt động theo cách tôi cần, nhưng nó xấu, quá mức hoặc một số thứ khác. Tôi đã xem xét các công thức và cố gắng viết một vài giải pháp, nhưng cuối cùng tôi cũng nhận được một lượng báo cáo tương tự.

Có một loại công thức toán học nào có lợi cho tôi trong trường hợp này hay là 16 nếu các câu lệnh được chấp nhận?

Để giải thích mã, đây là một loại trò chơi theo lượt đồng thời .. hai người chơi có bốn nút hành động và mỗi kết quả đến từ một mảng (0-3), nhưng các biến 'một' & 'hai' có thể là giao bất cứ điều gì nếu điều này giúp. Kết quả là, 0 = không thắng, 1 = p1 thắng, 2 = p2 thắng, 3 = cả hai thắng.

public int fightMath(int one, int two) {

    if(one == 0 && two == 0) { result = 0; }
    else if(one == 0 && two == 1) { result = 0; }
    else if(one == 0 && two == 2) { result = 1; }
    else if(one == 0 && two == 3) { result = 2; }
    else if(one == 1 && two == 0) { result = 0; }
    else if(one == 1 && two == 1) { result = 0; }
    else if(one == 1 && two == 2) { result = 2; }
    else if(one == 1 && two == 3) { result = 1; }
    else if(one == 2 && two == 0) { result = 2; }
    else if(one == 2 && two == 1) { result = 1; }
    else if(one == 2 && two == 2) { result = 3; }
    else if(one == 2 && two == 3) { result = 3; }
    else if(one == 3 && two == 0) { result = 1; }
    else if(one == 3 && two == 1) { result = 2; }
    else if(one == 3 && two == 2) { result = 3; }
    else if(one == 3 && two == 3) { result = 3; }

    return result;
}

1
@waqaslam: - Điều này có thể giúp câu lệnh chuyển đổi Java xử lý hai biến?
Rahul Tripathi

9
Chắc chắn có một số logic ở đây có thể được khái quát hơn là bắt buộc? Chắc chắn có một số chức năng f(a, b)mang lại câu trả lời trong trường hợp chung? Bạn đã không giải thích logic của phép tính do đó tất cả các câu trả lời chỉ là son môi trên một con lợn. Tôi sẽ bắt đầu bằng cách suy nghĩ lại nghiêm túc về logic chương trình của bạn, sử dụng intcờ cho các hành động là rất lỗi thời. enums có thể chứa logic và có tính mô tả, điều này sẽ cho phép bạn viết mã theo cách hiện đại hơn.
Boris the Spider

Sau khi đọc câu trả lời @Steve Benett được cung cấp trong câu hỏi thay thế được liên kết ở trên, tôi có thể cho rằng không có câu trả lời công thức thẳng nào cho vấn đề này vì về cơ bản nó giống như một cơ sở dữ liệu. Tôi đã cố gắng giải thích trong câu hỏi ban đầu rằng tôi đang tạo một trò chơi đơn giản (máy bay chiến đấu) và người dùng có một lựa chọn gồm 4 nút: blockHigh (0), blockLow (1), AttackHigh (2) và AttackLow (3). Những con số này được tổ chức trong một mảng cho đến khi cần thiết. Sau đó, chúng được sử dụng bởi hàm 'combatMath ()' để gọi các lựa chọn của playerOne đối với playerTwos để đưa ra kết quả. Không phát hiện va chạm thực tế.
TomFirth

9
Nếu bạn có câu trả lời, xin vui lòng gửi nó như vậy. Các cuộc thảo luận mở rộng trong các ý kiến ​​là khó theo dõi, đặc biệt là khi có liên quan đến mã. Nếu bạn muốn nói chuyện về việc liệu câu hỏi này nên đã được chuyển sang Mã Rà soát, có một Meta thảo luận về điều đó.
George Stocker

1
Bạn có ý nghĩa gì bởi "giống như một cơ sở dữ liệu"? Nếu các giá trị này nằm trong cơ sở dữ liệu, hãy kéo chúng từ đó. Mặt khác, nếu nó thực sự phức tạp, tôi sẽ để nó giống như bạn có nó và thêm nhận xét logic kinh doanh sau mỗi dòng để mọi người hiểu những gì đang diễn ra. Nó là tốt hơn (với tôi) lâu dài và rõ ràng - ai đó trong tương lai có thể hiểu những gì đang xảy ra. Nếu bạn đặt nó vào bản đồ hoặc cố gắng lưu 8 dòng mã, thì mặt trái thực sự rất nhỏ và kích thước lớn hơn: bạn làm cho nó ngày càng khó hiểu hơn đối với người cần đọc mã của bạn một ngày nào đó.
skaz

Câu trả lời:


600

Nếu bạn không thể đưa ra công thức, bạn có thể sử dụng bảng cho số lượng kết quả hạn chế như vậy:

final int[][] result = new int[][] {
  { 0, 0, 1, 2 },
  { 0, 0, 2, 1 },
  { 2, 1, 3, 3 },
  { 1, 2, 3, 3 }
};
return result[one][two];

7
Điều này thật thú vị vì tôi chưa từng thấy giải pháp này trước đây. Tôi không chắc là tôi hiểu kết quả trả về nhưng sẽ thích thử nghiệm điều đó.
TomFirth

4
Bạn không cần xác nhận, Java sẽ đưa ra một cách IndexOutOfBoundsExceptionnào đó nếu một hoặc nhiều chỉ số nằm ngoài giới hạn.
JAB

43
@JoeHarper Nếu bạn muốn thứ gì đó dễ đọc, bạn sẽ không sử dụng số ma thuật ở nơi đầu tiên và bạn sẽ có một bình luận giải thích về ánh xạ. Vì vậy, tôi thích phiên bản này hơn bản gốc, nhưng về lâu dài, tôi có thể sử dụng một cách tiếp cận liên quan đến các kiểu liệt kê hoặc ít nhất là các hằng số được đặt tên.
JAB

13
@JoeHarper "Về mặt lý thuyết" là một chuyện, "thực tế" là một chuyện khác. Tất nhiên, tôi cố gắng sử dụng cách đặt tên mô tả (ngoại trừ i/ j/ kquy ước cho các biến vòng lặp), các hằng số được đặt tên, sắp xếp mã của tôi theo kiểu có thể đọc được, v.v., nhưng khi tên biến và hàm bắt đầu chiếm hơn 20 ký tự mỗi tôi thấy nó thực sự dẫn đến mã ít đọc hơn. Cách tiếp cận thông thường của tôi là cố gắng tìm mã dễ hiểu nhưng cô đọng với các bình luận ở đây và ở đó để giải thích tại sao mã được cấu trúc theo cách của nó (so với cách thức). Đặt lý do tại sao trong tên chỉ làm xáo trộn mọi thứ.
JAB

13
Tôi thích giải pháp này cho vấn đề cụ thể này bởi vì các kết quả thực sự được quyết định bởi một ma trận kết quả.
Thử

201

Vì tập dữ liệu của bạn rất nhỏ, bạn có thể nén mọi thứ thành 1 số nguyên dài và biến nó thành công thức

public int fightMath(int one,int two)
{
   return (int)(0xF9F66090L >> (2*(one*4 + two)))%4;
}

Biến thể bitwise hơn:

Điều này sử dụng thực tế mọi thứ là bội số của 2

public int fightMath(int one,int two)
{
   return (0xF9F66090 >> ((one << 3) | (two << 1))) & 0x3;
}

Nguồn gốc của hằng số ma thuật

Tôi có thể nói gì? Thế giới cần phép thuật, đôi khi khả năng một cái gì đó kêu gọi sự sáng tạo của nó.

Bản chất của hàm giải quyết vấn đề của OP là ánh xạ từ 2 số (một, hai), miền {0,1,2,3} đến phạm vi {0,1,2,3}. Mỗi câu trả lời đã tiếp cận cách thực hiện bản đồ đó.

Ngoài ra, bạn có thể thấy trong một số câu trả lời phân chia lại vấn đề dưới dạng bản đồ 1 cơ sở 2 chữ số 4 số N (một, hai) trong đó một là chữ số 1, hai là chữ số 2 và N = 4 * một + hai; N = {0,1,2, ..., 15} - mười sáu giá trị khác nhau, đó là điều quan trọng. Đầu ra của hàm là một cơ sở 1 chữ số gồm 4 số {0,1,2,3} - 4 giá trị khác nhau, cũng rất quan trọng.

Bây giờ, một số cơ sở 4 chữ số có thể được biểu thị dưới dạng số cơ sở 2 chữ số 2; {0,1,2,3} = {00,01,10,11} và do đó, mỗi đầu ra có thể được mã hóa chỉ với 2 bit. Từ trên xuống, chỉ có 16 đầu ra khác nhau có thể, vì vậy 16 * 2 = 32 bit là tất cả những gì cần thiết để mã hóa toàn bộ bản đồ; tất cả có thể vừa với 1 số nguyên.

Hằng số M là mã hóa của bản đồ m trong đó m (0) được mã hóa theo bit M [0: 1], m (1) được mã hóa theo bit M [2: 3] và m (n) được mã hóa theo bit M [n * 2: n * 2 + 1].

Tất cả những gì còn lại là lập chỉ mục và trả về phần bên phải của hằng số, trong trường hợp này bạn có thể dịch chuyển M đúng 2 * N lần và lấy 2 bit có ý nghĩa nhỏ nhất, đó là (M >> 2 * N) & 0x3. Các biểu thức (một << 3) và (hai << 1) chỉ là nhân lên nhiều thứ trong khi lưu ý rằng 2 * x = x << 1 và 8 * x = x << 3.


79
thông minh, nhưng không ai khác đọc mã sẽ có cơ hội hiểu nó.
Aran Mulholland

106
Tôi nghĩ rằng đây là thực tế cực kỳ xấu. Không ai khác ngoại trừ tác giả sẽ hiểu điều đó. Bạn muốn nhìn vào một đoạn mã và hiểu nó một cách nhanh chóng. Nhưng đây chỉ là một sự lãng phí thời gian.
Balázs Németh

14
Tôi với @ BalázsMáriaNémeth về điều này. Mặc dù rất ấn tượng, bạn nên mã hóa cho những kẻ thái nhân cách bạo lực!
OrhanC1

90
Tất cả các downvoters nghĩ rằng đây là một mùi mã gớm ghiếc. Tất cả các upvoters đều nghĩ giống nhau, nhưng ngưỡng mộ sự thông minh đằng sau nó. +1 (Đừng bao giờ sử dụng mã này.)
usr

4
Thật là một ví dụ đáng yêu về việc chỉ viết mã !
giải đấu

98

Tôi không thích bất kỳ giải pháp nào được trình bày ngoại trừ JAB. Không ai trong số những người khác làm cho nó dễ dàng để đọc mã và hiểu những gì đang được tính toán .

Đây là cách tôi sẽ viết mã này - Tôi chỉ biết C #, không phải Java, nhưng bạn có được hình ảnh:

const bool t = true;
const bool f = false;
static readonly bool[,] attackResult = {
    { f, f, t, f }, 
    { f, f, f, t },
    { f, t, t, t },
    { t, f, t, t }
};
[Flags] enum HitResult 
{ 
    Neither = 0,
    PlayerOne = 1,
    PlayerTwo = 2,
    Both = PlayerOne | PlayerTwo
}
static HitResult ResolveAttack(int one, int two)
{
    return 
        (attackResult[one, two] ? HitResult.PlayerOne : HitResult.Neither) | 
        (attackResult[two, one] ? HitResult.PlayerTwo : HitResult.Neither);
}    

Bây giờ thì rõ ràng hơn những gì đang được tính toán ở đây: điều này nhấn mạnh rằng chúng ta đang tính toán những người bị tấn công bởi những gì tấn công và trả lại cả hai kết quả.

Tuy nhiên điều này có thể còn tốt hơn; mảng Boolean có phần mờ đục. Tôi thích cách tiếp cận tra cứu bảng nhưng tôi sẽ có xu hướng viết nó theo cách làm cho nó rõ ràng về ngữ nghĩa trò chơi dự định là gì. Đó là, thay vì "một cuộc tấn công bằng không và phòng thủ một kết quả không bị tấn công", thay vào đó, hãy tìm cách làm cho mã rõ ràng hơn ngụ ý "một cuộc tấn công đá thấp và phòng thủ khối thấp không gây ra cú đánh". Làm cho mã phản ánh logic kinh doanh của trò chơi.


66
Vô lý. Hầu hết các chương trình có kinh nghiệm nhẹ sẽ có thể đánh giá cao lời khuyên đang được đưa ra ở đây và áp dụng kiểu mã hóa cho ngôn ngữ riêng của họ. Câu hỏi là làm thế nào để tránh một chuỗi ifs. Điều này cho thấy làm thế nào.
GreenAsJade

6
@ user3414693: Tôi nhận thức rõ rằng đó là một câu hỏi Java. Nếu bạn đọc câu trả lời cẩn thận mà trở nên rõ ràng. Nếu bạn tin rằng câu trả lời của tôi là không khôn ngoan thì tôi khuyến khích bạn viết câu trả lời của riêng bạn mà bạn thích hơn.
Eric Lippert

1
@EricLippert Tôi cũng thích giải pháp của JAB. IMHO, loại enum trong C # để lại nhiều mong muốn. Nó không đi theo hố triết lý thành công mà các tính năng còn lại làm. Ví dụ: stackoverflow.com/a/847353/92414 Đội c # có kế hoạch nào để tạo một loại enum mới (để tránh phá vỡ mã hiện có) được thiết kế tốt hơn không?
SolutionYogi

@SolutionYogi: Tôi cũng không thích enum trong C #, mặc dù chúng là cách chúng có lý do lịch sử tốt. (Chủ yếu là để tương thích với các en COM COM hiện có.) Tôi không biết về bất kỳ kế hoạch nào để thêm thiết bị mới cho enum trong C # 6.
Eric Lippert

3
@SList không, bình luận không chạy. OP đã làm chính xác những gì nên làm; chuyển đổi ý kiến ​​để xóa mã. Xem ví dụ Steve McConnell Hoàn thành stevemcconnell.com/cccntnt.htm
djechlin

87

Bạn có thể tạo ma trận chứa kết quả

int[][] results = {{0, 0, 1, 2}, {0, 0, 2, 1},{2, 1, 3, 3},{2, 1, 3, 3}};

Khi bạn muốn nhận được giá trị, bạn sẽ sử dụng

public int fightMath(int one, int two) {
  return this.results[one][two]; 
}

69

Những người khác đã đề xuất ý tưởng ban đầu của tôi, phương thức ma trận, nhưng ngoài việc hợp nhất các câu lệnh if, bạn có thể tránh một số điều bạn có bằng cách đảm bảo các đối số được cung cấp nằm trong phạm vi dự kiến ​​và bằng cách sử dụng trả về tại chỗ (một số mã hóa các tiêu chuẩn mà tôi đã thấy thực thi một điểm thoát cho các hàm, nhưng tôi thấy rằng nhiều trả về rất hữu ích để tránh mã hóa mũi tên và với sự phổ biến của các ngoại lệ trong Java dù sao cũng không thực thi nghiêm ngặt quy tắc như vậy vì bất kỳ ngoại lệ chưa được lưu nào được ném bên trong phương thức là một điểm có thể thoát ra bằng mọi cách). Các câu lệnh chuyển đổi lồng nhau là một khả năng, nhưng đối với phạm vi giá trị nhỏ mà bạn đang kiểm tra ở đây tôi thấy nếu các câu lệnh nhỏ gọn hơn và không có khả năng dẫn đến sự khác biệt về hiệu suất,

public int fightMath(int one, int two) {
    if (one > 3 || one < 0 || two > 3 || two < 0) {
        throw new IllegalArgumentException("Result is undefined for arguments outside the range [0, 3]");
    }

    if (one <= 1) {
        if (two <= 1) return 0;
        if (two - one == 2) return 1;
        return 2; // two can only be 3 here, no need for an explicit conditional
    }

    // one >= 2
    if (two >= 2) return 3;
    if (two == 1) return 1;
    return 2; // two can only be 0 here
}

Điều này cuối cùng không thể đọc được hơn nó có thể là do sự bất thường của các phần của ánh xạ kết quả đầu vào->. Thay vào đó, tôi thích phong cách ma trận do tính đơn giản của nó và cách bạn có thể thiết lập ma trận để có ý nghĩa trực quan (mặc dù điều đó một phần bị ảnh hưởng bởi ký ức của tôi về bản đồ Karnaugh):

int[][] results = {{0, 0, 1, 2},
                   {0, 0, 2, 1},
                   {2, 1, 3, 3},
                   {2, 1, 3, 3}};

Cập nhật: Khi bạn đề cập đến việc chặn / đánh, đây là một thay đổi căn bản hơn đối với chức năng sử dụng các kiểu liệt kê được giữ đúng / thuộc tính cho các đầu vào và kết quả và cũng sửa đổi kết quả một chút để tính đến việc chặn, điều này sẽ dẫn đến nhiều hơn chức năng có thể đọc được.

enum MoveType {
    ATTACK,
    BLOCK;
}

enum MoveHeight {
    HIGH,
    LOW;
}

enum Move {
    // Enum members can have properties/attributes/data members of their own
    ATTACK_HIGH(MoveType.ATTACK, MoveHeight.HIGH),
    ATTACK_LOW(MoveType.ATTACK, MoveHeight.LOW),
    BLOCK_HIGH(MoveType.BLOCK, MoveHeight.HIGH),
    BLOCK_LOW(MoveType.BLOCK, MoveHeight.LOW);

    public final MoveType type;
    public final MoveHeight height;

    private Move(MoveType type, MoveHeight height) {
        this.type = type;
        this.height = height;
    }

    /** Makes the attack checks later on simpler. */
    public boolean isAttack() {
        return this.type == MoveType.ATTACK;
    }
}

enum LandedHit {
    NEITHER,
    PLAYER_ONE,
    PLAYER_TWO,
    BOTH;
}

LandedHit fightMath(Move one, Move two) {
    // One is an attack, the other is a block
    if (one.type != two.type) {
        // attack at some height gets blocked by block at same height
        if (one.height == two.height) return LandedHit.NEITHER;

        // Either player 1 attacked or player 2 attacked; whoever did
        // lands a hit
        if (one.isAttack()) return LandedHit.PLAYER_ONE;
        return LandedHit.PLAYER_TWO;
    }

    // both attack
    if (one.isAttack()) return LandedHit.BOTH;

    // both block
    return LandedHit.NEITHER;
}

Bạn thậm chí không phải thay đổi chức năng nếu bạn muốn thêm các khối / tấn công có độ cao hơn, chỉ là các enum; thêm các loại di chuyển có thể sẽ yêu cầu sửa đổi chức năng, mặc dù. Ngoài ra, EnumSets có thể mở rộng hơn so với sử dụng enum bổ sung làm thuộc tính của enum chính, ví dụ EnumSet<Move> attacks = EnumSet.of(Move.ATTACK_HIGH, Move.ATTACK_LOW, ...);và sau đó attacks.contains(move)hơn là move.type == MoveType.ATTACK, mặc dù sử dụng EnumSets có thể sẽ chậm hơn một chút so với kiểm tra bằng trực tiếp.


Đối với trường hợp khối thành công dẫn đến bộ đếm, bạn có thể thay thế if (one.height == two.height) return LandedHit.NEITHER;bằng

if (one.height == two.height) {
    // Successful block results in a counter against the attacker
    if (one.isAttack()) return LandedHit.PLAYER_TWO;
    return LandedHit.PLAYER_ONE;
}

Ngoài ra, việc thay thế một số ifcâu lệnh bằng cách sử dụng toán tử ternary ( boolean_expression ? result_if_true : result_if_false) có thể làm cho mã nhỏ gọn hơn (ví dụ: mã trong khối trước sẽ trở thành return one.isAttack() ? LandedHit.PLAYER_TWO : LandedHit.PLAYER_ONE;), nhưng điều đó có thể dẫn đến các onelin khó đọc hơn nên tôi sẽ không đọc được t đề nghị nó cho phân nhánh phức tạp hơn.


Tôi chắc chắn sẽ xem xét điều này nhưng mã hiện tại của tôi cho phép tôi sử dụng giá trị int của onetwođược sử dụng lại làm điểm bắt đầu trên spritesheet của tôi. Mặc dù nó không yêu cầu nhiều mã bổ sung để cho phép điều đó.
TomFirth

2
@ TomFirth84 Có một EnumMaplớp học mà bạn có thể sử dụng để lập bản đồ enums để bù đắp số nguyên của bạn (bạn cũng có thể sử dụng các giá trị thứ tự của các thành viên enum trực tiếp, ví dụ như Move.ATTACK_HIGH.ordinal()sẽ 0, Move.ATTACK_LOW.ordinal()sẽ là 1, vv, nhưng mà của mong manh hơn / ít linh hoạt hơn một cách rõ ràng liên kết mỗi thành viên với một giá trị như thêm các giá trị enum vào giữa các giá trị hiện có sẽ làm mất số đếm, điều này sẽ không xảy ra với một EnumMap.)
JAB

7
Đây là giải pháp dễ đọc nhất vì nó dịch mã thành thứ gì đó có ý nghĩa đối với người đọc mã.
David Stanley

Mã của bạn, ít nhất là mã sử dụng enums, là sai. Theo các câu lệnh if trong OP, một khối thành công dẫn đến một cú đánh vào kẻ tấn công. Nhưng +1 cho mã có ý nghĩa.
Taemyr

2
Bạn thậm chí có thể thêm một attack(against)phương thức vào Moveenum, trả lại HIT khi di chuyển là một cuộc tấn công thành công, BACKFIRE khi di chuyển là một cuộc tấn công bị chặn và KHÔNG NÊN khi đó không phải là một cuộc tấn công. Bằng cách này, bạn có thể triển khai nó nói chung ( public boolean attack(Move other) { if this.isAttack() return (other.isAttack() || other.height != this.height) ? HIT : BACKFIRE; return NOTHING; }) và ghi đè lên các bước di chuyển cụ thể khi cần thiết (di chuyển yếu mà bất kỳ khối nào cũng có thể chặn, các cuộc tấn công không bao giờ phản tác dụng, v.v.)
viết lại vào

50

Tại sao không sử dụng một mảng?

Tôi sẽ bắt đầu lại từ đầu. Tôi thấy một mẫu, các giá trị đi từ 0 đến 3 và bạn muốn bắt tất cả các giá trị có thể. Đây là bảng của bạn:

0 & 0 = 0
0 & 1 = 0
0 & 2 = 1
0 & 3 = 2
1 & 0 = 0
1 & 1 = 0
1 & 2 = 2
1 & 3 = 1
2 & 0 = 2
2 & 1 = 1
2 & 2 = 3
2 & 3 = 3
3 & 0 = 2
3 & 1 = 1
3 & 2 = 3
3 & 3 = 3

Khi chúng ta nhìn vào bảng nhị phân này, chúng ta sẽ thấy các kết quả sau:

00 & 00 = 00
00 & 01 = 00
00 & 10 = 01
00 & 11 = 10
01 & 00 = 00
01 & 01 = 00
01 & 10 = 10
01 & 11 = 01
10 & 00 = 10
10 & 01 = 01
10 & 10 = 11
10 & 11 = 11
11 & 00 = 10
11 & 01 = 01
11 & 10 = 11
11 & 11 = 11

Bây giờ có thể bạn đã thấy một số mẫu nhưng khi tôi kết hợp giá trị một và hai tôi thấy rằng bạn đang sử dụng tất cả các giá trị 0000, 0001, 0010, ..... 1110 và 1111. Bây giờ, hãy kết hợp giá trị một và hai để tạo một giá trị Số nguyên 4 bit.

0000 = 00
0001 = 00
0010 = 01
0011 = 10
0100 = 00
0101 = 00
0110 = 10
0111 = 01
1000 = 10
1001 = 01
1010 = 11
1011 = 11
1100 = 10
1101 = 01
1110 = 11
1111 = 11

Khi chúng tôi dịch lại thành giá trị thập phân, chúng tôi thấy một mảng các giá trị rất có thể trong đó một và hai kết hợp có thể được sử dụng làm chỉ mục:

0 = 0
1 = 0
2 = 1
3 = 2
4 = 0
5 = 0
6 = 2
7 = 1
8 = 2
9 = 1
10 = 3
11 = 3
12 = 2
13 = 1
14 = 3
15 = 3

Mảng là sau đó {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3}, trong đó chỉ mục của nó chỉ đơn giản là một và hai kết hợp.

Tôi không phải là một lập trình viên Java nhưng bạn có thể loại bỏ tất cả các câu lệnh if và chỉ viết nó ra dưới dạng như thế này:

int[] myIntArray = {0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 2, 1, 3, 3};
result = myIntArray[one * 4 + two]; 

Tôi không biết nếu một bithift bằng 2 nhanh hơn phép nhân. Nhưng nó có thể là giá trị một thử.


2
Độ dịch chuyển bit 2 gần như chắc chắn nhanh hơn bội số của 4. Tốt nhất, phép nhân với 4, sẽ nhận ra rằng 4 là 2 ^ 2 và tự dịch chuyển một chút (có khả năng được dịch bởi trình biên dịch). Thành thật mà nói, với tôi, sự thay đổi là dễ đọc hơn.
Cruncher

Tôi thích cách tiếp cận của bạn! Về cơ bản, nó làm phẳng một ma trận 4 x 4 thành một mảng 16 phần tử.
Cameron Tinker

6
Ngày nay, nếu tôi không nhầm, trình biên dịch chắc chắn sẽ nhận ra rằng bạn đang nhân với một sức mạnh của hai và tối ưu hóa nó theo đó. Vì vậy, đối với bạn, lập trình viên, sự thay đổi bit và phép nhân phải có hiệu suất chính xác như nhau.
Tanner Swett

24

Điều này sử dụng một chút bitmagic (bạn đã thực hiện bằng cách giữ hai bit thông tin (thấp / cao & tấn công / khối) trong một số nguyên duy nhất):

Tôi không chạy nó, chỉ gõ nó ở đây, xin vui lòng nhân đôi. Ý tưởng chắc chắn hoạt động. EDIT: Bây giờ nó đã được thử nghiệm cho mọi đầu vào, hoạt động tốt.

public int fightMath(int one, int two) {
    if(one<2 && two<2){ //both players blocking
        return 0; // nobody hits
    }else if(one>1 && two>1){ //both players attacking
        return 3; // both hit
    }else{ // some of them attack, other one blocks
        int different_height = (one ^ two) & 1; // is 0 if they are both going for the same height - i.e. blocker wins, and 1 if height is different, thus attacker wins
        int attacker = one>1?1:0; // is 1 if one is the attacker, two is the blocker, and 0 if one is the blocker, two is the attacker
        return (attacker ^ different_height) + 1;
    }
}

Hoặc tôi nên đề nghị tách hai bit thông tin thành các biến riêng biệt? Mã dựa chủ yếu vào các hoạt động bit như thế này ở trên thường rất khó để duy trì.


2
Tôi đồng ý với giải pháp này, trông rất giống những gì tôi đã nghĩ trong nhận xét của mình về câu hỏi chính. Tôi muốn chia nó thành các biến riêng biệt, điều đó sẽ giúp dễ dàng thêm một cuộc tấn công trung gian chẳng hạn trong tương lai.
Yoh

2
Tôi chỉ sửa một số lỗi trong đoạn mã trên sau khi kiểm tra nó, bây giờ nó hoạt động tốt. Đi xa hơn trên con đường của người thao tác bit, tôi cũng đã đưa ra một giải pháp một dòng, vẫn không quá bí ẩn như bitmask trong các câu trả lời khác, nhưng vẫn đủ khó để đánh return ((one ^ two) & 2) == 0 ? (one & 2) / 2 * 3 : ((one & 2) / 2 ^ ((one ^ two) & 1)) + 1;
lừa

1
Đây là câu trả lời tốt nhất vì bất kỳ lập trình viên mới nào đọc nó sẽ thực sự hiểu được phép thuật xảy ra đằng sau tất cả những con số ma thuật đó.
bezmax

20

Thành thật mà nói, mọi người đều có phong cách mã riêng. Tôi sẽ không nghĩ rằng hiệu suất sẽ bị ảnh hưởng quá nhiều. Nếu bạn hiểu điều này tốt hơn là sử dụng phiên bản vỏ hộp chuyển đổi, thì hãy tiếp tục sử dụng cái này.

Bạn có thể lồng các if, vì vậy có khả năng sẽ có một sự gia tăng hiệu suất nhẹ cho lần cuối của bạn nếu kiểm tra vì nó sẽ không trải qua nhiều câu lệnh if. Nhưng trong ngữ cảnh của một khóa học java cơ bản, nó có thể sẽ không có lợi.

else if(one == 3 && two == 3) { result = 3; }

Vì vậy, thay vì ...

if(one == 0 && two == 0) { result = 0; }
else if(one == 0 && two == 1) { result = 0; }
else if(one == 0 && two == 2) { result = 1; }
else if(one == 0 && two == 3) { result = 2; }

Bạn sẽ làm ...

if(one == 0) 
{ 
    if(two == 0) { result = 0; }
    else if(two == 1) { result = 0; }
    else if(two == 2) { result = 1; }
    else if(two == 3) { result = 2; }
}

Và chỉ định dạng lại nó như bạn muốn.

Điều này không làm cho mã trông đẹp hơn, nhưng có khả năng tăng tốc một chút tôi tin.


3
Không biết nếu nó thực sự tốt nhưng trong trường hợp này, tôi có thể sẽ sử dụng các câu lệnh chuyển đổi lồng nhau. Nó sẽ tốn nhiều không gian hơn nhưng nó sẽ thực sự rõ ràng.
dyesdyes

Điều đó cũng sẽ làm việc, nhưng tôi đoán đó là vấn đề ưu tiên. Tôi thực sự thích nếu các câu lệnh vì nó hầu như nói những gì mã đang làm. Tất nhiên không đưa ra ý kiến ​​của bạn, bất cứ điều gì làm việc cho bạn :). Upvote cho đề nghị thay thế mặc dù!
Joe Harper

12

Hãy xem những gì chúng ta biết

1: câu trả lời của bạn là đối xứng cho P1 (người chơi một) và P2 (người chơi hai). Điều này có ý nghĩa đối với một trò chơi chiến đấu nhưng cũng là thứ bạn có thể tận dụng để cải thiện logic của mình.

2: 3 nhịp 0 nhịp 2 nhịp 1 nhịp 3. Các trường hợp duy nhất không được bao gồm trong các trường hợp này là kết hợp 0 ​​vs 1 và 2 vs 3. Nói cách khác, bảng chiến thắng duy nhất trông như thế này: 0 nhịp 2, 1 nhịp 3, 2 nhịp 1, 3 nhịp 0.

3: Nếu 0/1 đi lên với nhau thì sẽ có một trận hòa không thành công nhưng nếu 2/3 đi ngược lại thì cả hai đều đánh

Trước tiên, chúng ta hãy xây dựng hàm một chiều cho chúng ta biết nếu chúng ta thắng:

// returns whether we beat our opponent
public boolean doesBeat(int attacker, int defender) {
  int[] beats = {2, 3, 1, 0};
  return defender == beats[attacker];
}

Sau đó chúng ta có thể sử dụng chức năng này để soạn kết quả cuối cùng:

// returns the overall fight result
// bit 0 = one hits
// bit 1 = two hits
public int fightMath(int one, int two)
{
  // Check to see whether either has an outright winning combo
  if (doesBeat(one, two))
    return 1;

  if (doesBeat(two, one))
    return 2;

  // If both have 0/1 then its hitless draw but if both have 2/3 then they both hit.
  // We can check this by seeing whether the second bit is set and we need only check
  // one's value as combinations where they don't both have 0/1 or 2/3 have already
  // been dealt with 
  return (one & 2) ? 3 : 0;
}

Mặc dù điều này phức tạp hơn và có thể chậm hơn so với tra cứu bảng được đưa ra trong nhiều câu trả lời, tôi tin rằng đây là một phương pháp ưu việt vì nó thực sự gói gọn logic của mã của bạn và mô tả nó cho bất kỳ ai đọc mã của bạn. Tôi nghĩ rằng điều này làm cho nó thực hiện tốt hơn.

(Đã được một thời gian kể từ khi tôi thực hiện bất kỳ Java nào nên xin lỗi nếu cú ​​pháp bị tắt, hy vọng nó vẫn dễ hiểu nếu tôi hiểu sai một chút)

Nhân tiện, 0-3 rõ ràng có ý nghĩa gì đó; chúng không phải là giá trị tùy ý nên sẽ giúp đặt tên cho chúng.


11

Tôi hy vọng tôi hiểu logic một cách chính xác. Làm thế nào về một cái gì đó như:

public int fightMath (int one, int two)
{
    int oneHit = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : 0;
    int twoHit = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : 0;

    return oneHit+twoHit;
}

Kiểm tra một lần đánh cao hoặc một lần hạ thấp không bị chặn và tương tự đối với người chơi hai.

Chỉnh sửa: Thuật toán chưa được hiểu đầy đủ, "hit" được trao khi chặn mà tôi không nhận ra (Thx elias):

public int fightMath (int one, int two)
{
    int oneAttack = ((one == 3 && two != 1) || (one == 2 && two != 0)) ? 1 : (one >= 2) ? 2 : 0;
    int twoAttack = ((two == 3 && one != 1) || (two == 2 && one != 0)) ? 2 : (two >= 2) ? 1 : 0;

    return oneAttack | twoAttack;
}

Cách để tìm ra kết quả tuyệt vời!
Chad

1
Tôi thích cách tiếp cận, nhưng tôi sợ giải pháp này hoàn toàn thiếu khả năng đánh bằng cách chặn một cuộc tấn công (ví dụ: nếu one = 0 và hai = 2 thì nó trả về 0, nhưng 1 được mong đợi theo thông số kỹ thuật). Có lẽ bạn có thể làm việc với nó để làm cho đúng, nhưng tôi không chắc rằng mã kết quả sẽ vẫn thanh lịch như vậy, vì điều này có nghĩa là các dòng sẽ phát triển dài hơn một chút.
elias

Không nhận ra rằng một "hit" đã được trao cho khối. Cảm ơn đã chỉ ra điều đó. Điều chỉnh với sửa chữa rất đơn giản.
Chris

10

Tôi không có kinh nghiệm với Java nên có thể có một số lỗi chính tả. Vui lòng coi mã là mã giả.

Tôi sẽ đi với một công tắc đơn giản. Đối với điều đó, bạn cần một đánh giá số duy nhất. Tuy nhiên, trong trường hợp này, kể từ 0 <= one < 4 <= 90 <= two < 4 <= 9, chúng ta có thể chuyển đổi cả int thành int đơn giản bằng cách nhân onevới 10 và thêm two. Sau đó sử dụng một công tắc trong số kết quả như thế này:

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 10
    int evaluate = one * 10 + two;

    switch(evaluate) {
        // I'd consider a comment in each line here and in the original code
        // for clarity
        case 0: result = 0; break;
        case 1: result = 0; break;
        case 1: result = 0; break;
        case 2: result = 1; break;
        case 3: result = 2; break;
        case 10: result = 0; break;
        case 11: result = 0; break;
        case 12: result = 2; break;
        case 13: result = 1; break;
        case 20: result = 2; break;
        case 21: result = 1; break;
        case 22: result = 3; break;
        case 23: result = 3; break;
        case 30: result = 1; break;
        case 31: result = 2; break;
        case 32: result = 3; break;
        case 33: result = 3; break;
    }

    return result;
}

Có một phương pháp ngắn khác mà tôi chỉ muốn chỉ ra như một mã lý thuyết. Tuy nhiên tôi sẽ không sử dụng nó bởi vì nó có một số phức tạp thêm mà bạn thường không muốn giải quyết. Sự phức tạp thêm đến từ cơ sở 4 , vì số đếm là 0, 1, 2, 3, 10, 11, 12, 13, 20, ...

public int fightMath(int one, int two) {
    // Convert one and two to a single variable in base 4
    int evaluate = one * 4 + two;

    allresults = new int[] { 0, 0, 1, 2, 0, 0, 2, 1, 2, 1, 3, 3, 1, 2, 3, 3 };

    return allresults[evaluate];
}

Thực sự chỉ là ghi chú bổ sung, trong trường hợp tôi thiếu thứ gì đó từ Java. Trong PHP tôi sẽ làm:

function fightMath($one, $two) {
    // Convert one and two to a single variable in base 4
    $evaluate = $one * 10 + $two;

    $allresults = array(
         0 => 0,  1 => 0,  2 => 1,  3 => 2,
        10 => 0, 11 => 0, 12 => 2, 13 => 1,
        20 => 2, 21 => 1, 22 => 3, 23 => 3,
        30 => 1, 31 => 2, 32 => 3, 33 => 3 );

    return $allresults[$evaluate];
}

Java không có lamdas trước phiên bản thứ 8
Kirill Gamazkov 20/03/2016

1
Điều này. Đối với số lượng đầu vào nhỏ như vậy, tôi sẽ sử dụng một công tắc có giá trị tổng hợp (mặc dù nó có thể dễ đọc hơn với hệ số nhân lớn hơn 10, chẳng hạn như 100 hoặc 1000).
Medinoc

7

Vì bạn thích các ifđiều kiện lồng nhau , đây là một cách khác.
Lưu ý rằng nó không sử dụng resultthành viên và nó không thay đổi bất kỳ trạng thái nào.

public int fightMath(int one, int two) {
    if (one == 0) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 1; }
      if (two == 3) { return 2; }
    }   
    if (one == 1) {
      if (two == 0) { return 0; }
      if (two == 1) { return 0; }
      if (two == 2) { return 2; }
      if (two == 3) { return 1; }
    }
    if (one == 2) {
      if (two == 0) { return 2; }
      if (two == 1) { return 1; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    if (one == 3) {
      if (two == 0) { return 1; }
      if (two == 1) { return 2; }
      if (two == 2) { return 3; }
      if (two == 3) { return 3; }
    }
    return DEFAULT_RESULT;
}

Tại sao bạn không có ai khác?
FDinoff

3
@FDinoff Tôi có thể đã sử dụng elsechuỗi nhưng nó không tạo ra sự khác biệt.
Nick Dandoulakis

1
Tôi biết nó là tầm thường, nhưng sẽ không thêm các chuỗi khác thực thi nhanh hơn? 3 trong 4 trường hợp? Tôi luôn tạo thói quen viết mã để thực thi nhanh nhất có thể ngay cả khi chỉ là một vài chu kỳ.
Brandon Bearden

2
@BrandonBearden ở đây họ sẽ không tạo ra bất kỳ sự khác biệt nào (giả sử đầu vào luôn nằm trong phạm vi 0..3). Trình biên dịch có thể sẽ tối ưu hóa vi mã. Nếu chúng ta có một chuỗi các else ifcâu lệnh dài, chúng ta có thể tăng tốc mã bằng switchhoặc tra cứu các bảng.
Nick Dandoulakis

Làm thế nào là như vậy? Nếu one==0nó sẽ chạy mã, thì nó sẽ phải kiểm tra xem one==1sau đó nếu one==2và cuối cùng nếu one==3- Nếu có khác nếu ở đó, nó sẽ không thực hiện ba kiểm tra cuối cùng vì nó sẽ thoát khỏi câu lệnh sau trận đấu đầu tiên. Và có, bạn có thể tối ưu hóa hơn nữa bằng cách sử dụng câu lệnh chuyển đổi thay cho if (one...câu lệnh và sau đó tiếp tục sử dụng một công tắc khác trong trường hợp của one's. Tuy nhiên, đó không phải là câu hỏi của tôi.
Brandon Bearden

6

Hãy thử nó với vỏ chuyển đổi. ..

Hãy xem ở đây hoặc ở đây để biết thêm về nó

switch (expression)
{ 
  case constant:
        statements;
        break;
  [ case constant-2:
        statements;
        break;  ] ...
  [ default:
        statements;
        break;  ] ...
}

Bạn có thể thêm nhiều điều kiện (không đồng thời) vào nó và thậm chí có một tùy chọn mặc định trong đó không có trường hợp nào khác được thỏa mãn.

PS: Chỉ khi một điều kiện được thỏa mãn ..

Nếu 2 điều kiện phát sinh đồng thời .. Tôi không nghĩ chuyển đổi có thể được sử dụng. Nhưng bạn có thể giảm mã của bạn ở đây.

Câu lệnh chuyển đổi Java nhiều trường hợp


6

Điều đầu tiên xảy ra với tôi về cơ bản là cùng một câu trả lời được đưa ra bởi Francisco Presencia, nhưng được tối ưu hóa phần nào:

public int fightMath(int one, int two)
{
    switch (one*10 + two)
    {
    case  0:
    case  1:
    case 10:
    case 11:
        return 0;
    case  2:
    case 13:
    case 21:
    case 30:
        return 1;
    case  3:
    case 12:
    case 20:
    case 31:
        return 2;
    case 22:
    case 23:
    case 32:
    case 33:
        return 3;
    }
}

Bạn có thể tối ưu hóa thêm bằng cách đặt trường hợp cuối cùng (cho 3) trường hợp mặc định:

    //case 22:
    //case 23:
    //case 32:
    //case 33:
    default:
        return 3;

Ưu điểm của phương pháp này là dễ dàng hơn để xem giá trị nào onetwotương ứng với giá trị nào trả về so với một số phương thức được đề xuất khác.


Đây là một biến thể của một câu trả lời khác của tôi ở đây .
David R Tribble

6
((two&2)*(1+((one^two)&1))+(one&2)*(2-((one^two)&1)))/2

4
Mất bao lâu để bạn đến đây?
mbatchkarov

2
@mbatchkarov Khoảng 10 phút đọc các câu trả lời khác, sau đó 10 phút viết nguệch ngoạc bằng bút chì và giấy.
Dawood ibn Kareem

7
Tôi sẽ rất buồn nếu phải duy trì điều này.
Meryovi

uhmmm ... Có một lỗi: bạn đang mất tích; --unicorns
Alberto

Tôi đồng ý với @Meryovi, đạo cụ vì ngắn gọn, nhưng khủng khiếp như mã APL
Ed Griebel


3

Khi tôi vẽ một bảng giữa một / hai và kết quả, tôi thấy một mẫu,

if(one<2 && two <2) result=0; return;

Ở trên sẽ cắt giảm ít nhất 3 nếu báo cáo. Tôi không thấy một mẫu thiết lập cũng như tôi không thể lượm lặt được nhiều từ mã được đưa ra - nhưng nếu logic đó có thể được dẫn xuất, nó sẽ cắt giảm một số câu lệnh if.

Hi vọng điêu nay co ich.


3

Một điểm tốt sẽ là xác định các quy tắc như văn bản, sau đó bạn có thể dễ dàng rút ra công thức chính xác. Điều này được trích xuất từ ​​đại diện mảng đẹp của laalto:

{ 0, 0, 1, 2 },
{ 0, 0, 2, 1 },
{ 2, 1, 3, 3 },
{ 1, 2, 3, 3 }

Và ở đây chúng tôi đi với một số ý kiến ​​chung, nhưng bạn nên mô tả chúng theo các quy tắc:

if(one<2) // left half
{
    if(two<2) // upper left half
    {
        result = 0; //neither hits
    }
    else // lower left half
    {
        result = 1+(one+two)%2; //p2 hits if sum is even
    }
}
else // right half
{
    if(two<2) // upper right half
    {
        result = 1+(one+two+1)%2; //p1 hits if sum is even
    }
    else // lower right half
    {
        return 3; //both hit
    }
}

Tất nhiên bạn có thể giảm bớt mã này xuống ít mã hơn, nhưng nói chung nên hiểu mã của bạn hơn là tìm một giải pháp nhỏ gọn.

if((one<2)&&(two<2)) result = 0; //top left
else if((one>1)&&(two>1)) result = 3; //bottom right
else result = 1+(one+two+((one>1)?1:0))%2; //no idea what that means

Một số lời giải thích về các lượt truy cập p1 / p2 phức tạp sẽ rất tuyệt, trông thật thú vị!


3

Giải pháp ngắn nhất và vẫn có thể đọc được:

static public int fightMath(int one, int two)
{
    if (one < 2 && two < 2) return 0;
    if (one > 1 && two > 1) return 3;
    int n = (one + two) % 2;
    return one < two ? 1 + n : 2 - n;
}

hoặc thậm chí ngắn hơn:

static public int fightMath(int one, int two)
{
    if (one / 2 == two / 2) return (one / 2) * 3;
    return 1 + (one + two + one / 2) % 2;
}

Không chứa bất kỳ số "ma thuật" nào;) Hy vọng nó sẽ giúp.


2
Các công thức như thế này sẽ làm cho không thể thay đổi (cập nhật) kết quả của sự kết hợp vào một ngày sau đó. Cách duy nhất sẽ là làm lại toàn bộ công thức.
SNag

2
@SNag: Tôi đồng ý với điều đó. Giải pháp linh hoạt nhất là sử dụng mảng 2D. Nhưng tác giả của bài đăng này muốn có một công thức và đây là công thức tốt nhất bạn có thể nhận được chỉ với ifs và math đơn giản.
PW


1

Cá nhân tôi thích xếp tầng các nhà khai thác ternary:

int result = condition1
    ? result1
    : condition2
    ? result2
    : condition3
    ? result3
    : resultElse;

Nhưng trong trường hợp của bạn, bạn có thể sử dụng:

final int[] result = new int[/*16*/] {
    0, 0, 1, 2,
    0, 0, 2, 1,
    2, 1, 3, 3,
    1, 2, 3, 3
};

public int fightMath(int one, int two) {
    return result[one*4 + two];
}

Hoặc, bạn có thể nhận thấy một mẫu theo bit:

one   two   result

section 1: higher bits are equals =>
both result bits are equals to that higher bits

00    00    00
00    01    00
01    00    00
01    01    00
10    10    11
10    11    11
11    10    11
11    11    11

section 2: higher bits are different =>
lower result bit is inverse of lower bit of 'two'
higher result bit is lower bit of 'two'

00    10    01
00    11    10
01    10    10
01    11    01
10    00    10
10    01    01
11    00    01
11    01    10

Vì vậy, bạn có thể sử dụng phép thuật:

int fightMath(int one, int two) {
    int b1 = one & 2, b2 = two & 2;
    if (b1 == b2)
        return b1 | (b1 >> 1);

    b1 = two & 1;

    return (b1 << 1) | (~b1);
}

1

Đây là một phiên bản khá súc tích, tương tự như phản hồi của JAB . Điều này sử dụng một bản đồ để lưu trữ mà di chuyển chiến thắng trên những người khác.

public enum Result {
  P1Win, P2Win, BothWin, NeitherWin;
}

public enum Move {
  BLOCK_HIGH, BLOCK_LOW, ATTACK_HIGH, ATTACK_LOW;

  static final Map<Move, List<Move>> beats = new EnumMap<Move, List<Move>>(
      Move.class);

  static {
    beats.put(BLOCK_HIGH, new ArrayList<Move>());
    beats.put(BLOCK_LOW, new ArrayList<Move>());
    beats.put(ATTACK_HIGH, Arrays.asList(ATTACK_LOW, BLOCK_LOW));
    beats.put(ATTACK_LOW, Arrays.asList(ATTACK_HIGH, BLOCK_HIGH));
  }

  public static Result compare(Move p1Move, Move p2Move) {
    boolean p1Wins = beats.get(p1Move).contains(p2Move);
    boolean p2Wins = beats.get(p2Move).contains(p1Move);

    if (p1Wins) {
      return (p2Wins) ? Result.BothWin : Result.P1Win;
    }
    if (p2Wins) {
      return (p1Wins) ? Result.BothWin : Result.P2Win;
    }

    return Result.NeitherWin;
  }
} 

Thí dụ:

System.out.println(Move.compare(Move.ATTACK_HIGH, Move.BLOCK_LOW));

Bản in:

P1Win

Tôi muốn giới thiệu static final Map<Move, List<Move>> beats = new java.util.EnumMap<>();thay vào đó, nó sẽ hiệu quả hơn một chút.
JAB

@JAB Vâng, ý kiến ​​hay. Tôi luôn quên kiểu đó tồn tại. Và ... thật khó xử khi xây dựng nó!
Duncan Jones

1

Tôi sẽ sử dụng Bản đồ, là HashMap hoặc TreeMap

Đặc biệt là nếu các tham số không có trong mẫu 0 <= X < N

Giống như một tập hợp các số nguyên dương ngẫu nhiên ..

public class MyMap
{
    private TreeMap<String,Integer> map;

    public MyMap ()
    {
        map = new TreeMap<String,Integer> ();
    }

    public void put (int key1, int key2, Integer value)
    {
        String key = (key1+":"+key2);

        map.put(key, new Integer(value));
    }

    public Integer get (int key1, int key2)
    {
        String key = (key1+":"+key2);

        return map.get(key);
    }
}

1

Cảm ơn @Joe Harper khi tôi kết thúc bằng cách sử dụng một biến thể câu trả lời của anh ấy. Để làm giảm nó xuống hơn nữa vì 2 kết quả trên 4 là như nhau, tôi giảm nó xuống hơn nữa.

Tôi có thể quay lại vấn đề này vào một lúc nào đó, nhưng nếu không có sự kháng cự lớn nào gây ra bởi nhiều tình ifhuống thì tôi sẽ giữ điều này ngay bây giờ. Tôi sẽ xem xét ma trận bảng và chuyển đổi các giải pháp câu lệnh hơn nữa.

public int fightMath(int one, int two) {
  if (one === 0) {
    if (two === 2) { return 1; }
    else if(two === 3) { return 2; }
    else { return 0; }
  } else if (one === 1) {
    if (two === 2) { return 2; }
    else if (two === 3) { return 1; }
    else { return 0; }
  } else if (one === 2) {
    if (two === 0) { return 2; }
    else if (two === 1) { return 1; }
    else { return 3; }
  } else if (one === 3) {
    if (two === 0) { return 1; }
    else if (two === 1) { return 2; }
    else { return 3; }
  }
}

13
Điều này thực sự ít đọc hơn bản gốc và không làm giảm số lượng câu lệnh if ...
Chad

@Chad Ý tưởng của việc này là tăng tốc độ xử lý và mặc dù có vẻ khủng khiếp nhưng nó dễ dàng được cập nhật nếu tôi thêm nhiều hành động trong tương lai. Nói điều này bây giờ tôi đang sử dụng một câu trả lời trước đây mà trước đây tôi không hiểu hết.
TomFirth

3
@ TomFirth84 Có lý do nào khiến bạn không tuân theo các quy ước mã hóa phù hợp cho các câu lệnh if của mình không?
ylun.ca

@ylun: Tôi đã giảm các dòng trước khi dán nó trên SO, không phải vì dễ đọc mà vì không gian spam tuyệt đối. Có nhiều biến thể thực hành trên trang này và thật đáng buồn, đó chỉ là cách tôi đã học và cảm thấy thoải mái.
TomFirth

2
@ TomFirth84 Tôi không nghĩ rằng điều này dễ dàng được cập nhật, số lượng dòng phát triển giống như sản phẩm của số lượng giá trị được phép.
Andrew Lazarus

0
  1. Sử dụng hằng hoặc enum để làm cho mã dễ đọc hơn
  2. Cố gắng chia mã thành nhiều chức năng hơn
  3. Cố gắng sử dụng tính đối xứng của vấn đề

Đây là một gợi ý làm thế nào điều này có thể trông như thế nào, nhưng sử dụng một số nguyên ở đây vẫn còn hơi xấu:

static final int BLOCK_HIGH = 0;
static final int BLOCK_LOW = 1;
static final int ATTACK_HIGH = 2;
static final int ATTACK_LOW = 3;

public static int fightMath(int one, int two) {
    boolean player1Wins = handleAttack(one, two);
    boolean player2Wins = handleAttack(two, one);
    return encodeResult(player1Wins, player2Wins); 
}



private static boolean handleAttack(int one, int two) {
     return one == ATTACK_HIGH && two != BLOCK_HIGH
        || one == ATTACK_LOW && two != BLOCK_LOW
        || one == BLOCK_HIGH && two == ATTACK_HIGH
        || one == BLOCK_LOW && two == ATTACK_LOW;

}

private static int encodeResult(boolean player1Wins, boolean player2Wins) {
    return (player1Wins ? 1 : 0) + (player2Wins ? 2 : 0);
}

Nó sẽ tốt hơn để sử dụng một loại có cấu trúc cho đầu vào và đầu ra. Đầu vào thực sự có hai trường: vị trí và loại (chặn hoặc tấn công). Đầu ra cũng có hai trường: player1Wins và player2Wins. Mã hóa này thành một số nguyên duy nhất làm cho việc đọc mã khó hơn.

class PlayerMove {
    PlayerMovePosition pos;
    PlayerMoveType type;
}

enum PlayerMovePosition {
    HIGH,LOW
}

enum PlayerMoveType {
    BLOCK,ATTACK
}

class AttackResult {
    boolean player1Wins;
    boolean player2Wins;

    public AttackResult(boolean player1Wins, boolean player2Wins) {
        this.player1Wins = player1Wins;
        this.player2Wins = player2Wins;
    }
}

AttackResult fightMath(PlayerMove a, PlayerMove b) {
    return new AttackResult(isWinningMove(a, b), isWinningMove(b, a));
}

boolean isWinningMove(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.ATTACK && !successfulBlock(b, a)
            || successfulBlock(a, b);
}

boolean successfulBlock(PlayerMove a, PlayerMove b) {
    return a.type == PlayerMoveType.BLOCK 
            && b.type == PlayerMoveType.ATTACK 
            && a.pos == b.pos;
}

Thật không may, Java không giỏi trong việc thể hiện các loại dữ liệu đó.


-2

Thay vào đó hãy làm một cái gì đó như thế này

   public int fightMath(int one, int two) {
    return Calculate(one,two)

    }


    private int Calculate(int one,int two){

    if (one==0){
        if(two==0){
     //return value}
    }else if (one==1){
   // return value as per condtiion
    }

    }

4
Bạn vừa tạo một chức năng riêng được bao bọc bởi công chúng. Tại sao không chỉ thực hiện điều này trong chức năng công cộng?
Martin Uting

5
Và bạn đã không giảm số lượng câu lệnh if.
Chad
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.