Lợi thế của chuyển đổi so với câu lệnh if-other


168

Cách thực hành tốt nhất để sử dụng một switchtuyên bố so với sử dụng một iftuyên bố trong 30 unsignedbảng liệt kê trong đó khoảng 10 có một hành động dự kiến ​​(hiện tại đó là cùng một hành động). Hiệu suất và không gian cần được xem xét nhưng không quan trọng. Tôi đã tóm tắt đoạn trích để không ghét tôi vì các quy ước đặt tên.

switch tuyên bố:

// numError is an error enumeration type, with 0 being the non-error case
// fire_special_event() is a stub method for the shared processing

switch (numError)
{  
  case ERROR_01 :  // intentional fall-through
  case ERROR_07 :  // intentional fall-through
  case ERROR_0A :  // intentional fall-through
  case ERROR_10 :  // intentional fall-through
  case ERROR_15 :  // intentional fall-through
  case ERROR_16 :  // intentional fall-through
  case ERROR_20 :
  {
     fire_special_event();
  }
  break;

  default:
  {
    // error codes that require no additional action
  }
  break;       
}

if tuyên bố:

if ((ERROR_01 == numError)  ||
    (ERROR_07 == numError)  ||
    (ERROR_0A == numError)  || 
    (ERROR_10 == numError)  ||
    (ERROR_15 == numError)  ||
    (ERROR_16 == numError)  ||
    (ERROR_20 == numError))
{
  fire_special_event();
}

26
Điều này được chỉnh sửa là 'chủ quan'? Có thật không? Chắc chắn 'chủ quan' là dành cho những thứ không thể được chứng minh bằng cách này hay cách khác?
Alexandra Franks

Chắc chắn bạn có thể thấy nó từ điểm tạo ra mã hiệu quả nhất, nhưng bất kỳ trình biên dịch hiện đại nào cũng phải hiệu quả như nhau. Cuối cùng, đây là một câu hỏi về màu sắc của chiếc xe đạp đổ.
jfs

8
Tôi không đồng ý, tôi không nghĩ đây là chủ quan. Một sự khác biệt ASM đơn giản là vấn đề, bạn không thể bỏ qua một vài giây tối ưu hóa trong nhiều trường hợp. Và trong câu hỏi này, đó không phải là một cuộc chiến tranh hay tranh luận tôn giáo, có lời giải thích hợp lý về lý do tại sao một người sẽ nhanh hơn, chỉ cần đọc câu trả lời được chấp nhận.
chakrit


@RichardFranks offtopic: grats! bạn là người đầu tiên được kiểm duyệt trên SO tôi từng thấy
jung_mole

Câu trả lời:


162

Sử dụng công tắc.

Trong trường hợp xấu nhất, trình biên dịch sẽ tạo cùng mã với chuỗi if-if, vì vậy bạn không mất gì cả. Nếu nghi ngờ, hãy đặt các trường hợp phổ biến nhất trước tiên vào câu lệnh switch.

Trong trường hợp tốt nhất, trình tối ưu hóa có thể tìm ra cách tốt hơn để tạo mã. Những điều phổ biến mà trình biên dịch thực hiện là xây dựng cây quyết định nhị phân (lưu so sánh và nhảy trong trường hợp trung bình) hoặc đơn giản là xây dựng bảng nhảy (hoạt động mà không so sánh gì cả).


2
Về mặt kỹ thuật vẫn sẽ có một so sánh, để đảm bảo giá trị của enum nằm trong bảng nhảy.
0124816

Jep. Đúng. Bật enum và xử lý tất cả các trường hợp có thể thoát khỏi so sánh cuối cùng mặc dù.
Nils Pipenbrinck

4
Lưu ý rằng một loạt các if về mặt lý thuyết có thể được phân tích giống như một công tắc bởi trình biên dịch, nhưng tại sao lại có cơ hội? Bằng cách sử dụng một công tắc, bạn đang truyền đạt chính xác những gì bạn muốn, điều này làm cho việc tạo mã dễ dàng hơn.
jakobengblom2

5
jakoben: Điều đó có thể được thực hiện, nhưng chỉ đối với các chuỗi if / if khác. Trong thực tế những điều này không xảy ra vì lập trình viên sử dụng chuyển đổi. Tôi đào sâu vào công nghệ trình biên dịch và tin tưởng tôi: Việc tìm kiếm các cấu trúc "vô dụng" như vậy tốn rất nhiều thời gian. Đối với những người biên dịch như vậy một tối ưu hóa không có ý nghĩa.
Nils Pipenbrinck

5
@NilsPipenbrinck với việc dễ dàng xây dựng đệ quy giả if- elsechuỗi trong lập trình meta mẫu và khó tạo switch casechuỗi, việc ánh xạ có thể trở nên quan trọng hơn. (và vâng, bình luận cổ xưa, nhưng web là mãi mãi, hoặc ít nhất là cho đến thứ ba tiếp theo)
Yakk - Adam Nevraumont

45

Đối với trường hợp đặc biệt mà bạn đã cung cấp trong ví dụ của mình, mã rõ ràng nhất có thể là:

if (RequiresSpecialEvent(numError))
    fire_special_event();

Rõ ràng điều này chỉ chuyển vấn đề sang một khu vực khác của mã, nhưng bây giờ bạn có cơ hội sử dụng lại bài kiểm tra này. Bạn cũng có nhiều lựa chọn hơn cho cách giải quyết nó. Bạn có thể sử dụng std :: set, ví dụ:

bool RequiresSpecialEvent(int numError)
{
    return specialSet.find(numError) != specialSet.end();
}

Tôi không gợi ý rằng đây là cách triển khai tốt nhất của RequestecialEvent, chỉ là đó là một lựa chọn. Bạn vẫn có thể sử dụng một công tắc hoặc chuỗi if-if, hoặc bảng tra cứu hoặc một số thao tác bit trên giá trị, bất cứ điều gì. Quá trình quyết định của bạn càng trở nên mơ hồ, bạn càng nhận được nhiều giá trị từ việc có nó trong một chức năng riêng biệt.


5
Đúng đấy. Khả năng đọc tốt hơn nhiều so với cả chuyển đổi và câu lệnh if. Tôi thực sự sẽ tự trả lời một cái gì đó như thế này, nhưng bạn đã đánh bại tôi. :-)
mlarsen

Nếu các giá trị enum của bạn đều nhỏ, thì bạn không cần hàm băm, chỉ cần một bảng. ví dụ: const std::bitset<MAXERR> specialerror(initializer); Sử dụng nó với if (specialerror[numError]) { fire_special_event(); }. Nếu bạn muốn kiểm tra giới hạn, bitset::test(size_t)sẽ đưa ra một ngoại lệ đối với các giá trị ngoài giới hạn. ( bitset::operator[]không kiểm tra phạm vi). cplusplus.com/reference/bitset/bitset/test . Điều này có thể sẽ tốt hơn so với triển khai bảng nhảy do trình biên dịch tạo ra switch, đặc biệt. trong trường hợp không đặc biệt, đây sẽ là một nhánh không lấy.
Peter Cordes

@PeterCordes Tôi vẫn lập luận rằng tốt hơn là đặt bảng vào chức năng của chính nó. Như tôi đã nói, có rất nhiều lựa chọn mở ra khi bạn làm điều đó, tôi đã không cố gắng liệt kê tất cả.
Đánh dấu tiền chuộc

@MarkRansom: Tôi không có ý không đồng ý với việc trừu tượng hóa nó. Ngay từ khi bạn đưa ra một triển khai mẫu bằng cách sử dụng std::set, tôi nghĩ rằng tôi chỉ ra rằng đó có thể là một lựa chọn kém. Hóa ra gcc đã biên dịch mã của OP để kiểm tra bitmap ngay lập tức 32 bit. trời ơi: goo.gl/qjjv0e . gcc 5.2 thậm chí sẽ làm điều này cho ifphiên bản. Ngoài ra, gcc gần đây sẽ sử dụng hướng dẫn kiểm tra bit btthay vì dịch chuyển để đặt một 1bit vào đúng vị trí và sử dụng test reg, imm32.
Peter Cordes

Bitmap liên tục ngay lập tức này là một chiến thắng lớn, bởi vì không có lỗi bộ nhớ cache trên bitmap. Nó hoạt động nếu tất cả các mã lỗi "đặc biệt" nằm trong phạm vi 64 trở xuống. (hoặc 32 cho mã 32 bit kế thừa.) Trình biên dịch trừ giá trị trường hợp nhỏ nhất, nếu nó khác không. Điều đáng nói là các trình biên dịch gần đây đủ thông minh để bạn có thể nhận được mã tốt từ bất kỳ logic nào bạn sử dụng, trừ khi bạn bảo nó sử dụng cấu trúc dữ liệu cồng kềnh.
Peter Cordes

24

Việc chuyển đổi nhanh hơn.

Chỉ cần thử if / other-ing 30 giá trị khác nhau trong một vòng lặp và so sánh nó với cùng một mã bằng cách sử dụng công tắc để xem công tắc nhanh hơn bao nhiêu.

Bây giờ, công tắc có một vấn đề thực sự : Công tắc phải biết tại thời điểm biên dịch các giá trị bên trong mỗi trường hợp. Điều này có nghĩa là đoạn mã sau:

// WON'T COMPILE
extern const int MY_VALUE ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

sẽ không biên dịch.

Hầu hết mọi người sau đó sẽ sử dụng định nghĩa (Aargh!) Và những người khác sẽ khai báo và định nghĩa các biến không đổi trong cùng một đơn vị biên dịch. Ví dụ:

// WILL COMPILE
const int MY_VALUE = 25 ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

Vì vậy, cuối cùng, nhà phát triển phải chọn giữa "tốc độ + độ rõ" so với "khớp mã".

(Không phải là một công tắc không thể được viết thành khó hiểu như địa ngục ... Hầu hết các công tắc tôi hiện đang thấy là thuộc loại "khó hiểu" này ... Nhưng đây là một câu chuyện khác ...)

Chỉnh sửa 2008-09-21:

bk1e đã thêm nhận xét sau: " Xác định các hằng số là enum trong tệp tiêu đề là một cách khác để xử lý việc này".

Tất nhiên là thế rồi.

Điểm của một kiểu bên ngoài là tách rời giá trị từ nguồn. Xác định giá trị này dưới dạng macro, dưới dạng khai báo const int đơn giản hoặc thậm chí là enum có tác dụng phụ là nội tuyến giá trị. Do đó, nếu định nghĩa, giá trị enum hoặc giá trị const int thay đổi, sẽ cần phải biên dịch lại. Khai báo bên ngoài có nghĩa là không cần phải biên dịch lại trong trường hợp thay đổi giá trị, nhưng mặt khác, làm cho nó không thể sử dụng chuyển đổi. Kết luận là Sử dụng chuyển đổi sẽ tăng khớp nối giữa mã chuyển đổi và các biến được sử dụng làm trường hợp . Khi nó là Ok, sau đó sử dụng chuyển đổi. Khi đó không phải là bất ngờ.

.

Chỉnh sửa 2013-01-15:

Vlad Lazarenko nhận xét về câu trả lời của tôi, đưa ra một liên kết đến nghiên cứu chuyên sâu của ông về mã lắp ráp được tạo ra bởi một công tắc. Rất giác ngộ: http://lazarenko.me/switch/


Xác định các hằng số là enum trong tệp tiêu đề là một cách khác để xử lý việc này.
bk1e


1
@Vlad Lazarenko: Cảm ơn đã liên kết! Đó là một đọc rất thú vị.
paercebal

1
Liên kết @AHmedHussein user404725 đã chết. Rất may, tôi đã tìm thấy nó trong WayBack Machine: web.archive.org/web/20131111091431/http://lazarenko.me/2013/01/ . Thật vậy, WayBack Machine có thể là một phước lành.
Jack Giffin

Cảm ơn rất nhiều, nó rất hữu ích
Ahmed Hussein

20

Trình biên dịch sẽ tối ưu hóa nó bằng mọi cách - chuyển sang phần chuyển đổi vì nó dễ đọc nhất.


3
Rất có thể trình biên dịch sẽ không chạm vào if-then-other. Trong thực tế, gccsẽ không làm điều đó chắc chắn (có một lý do tốt cho điều đó). Clang sẽ tối ưu hóa cả hai trường hợp thành một tìm kiếm nhị phân. Ví dụ, xem điều này .

7

Switch, nếu chỉ để đọc. Theo tôi, rất lớn nếu các tuyên bố khó duy trì và khó đọc hơn.

ERROR_01 : // cố ý rơi

hoặc là

(ERROR_01 == numError) ||

Cái sau dễ bị lỗi hơn và yêu cầu gõ và định dạng nhiều hơn cái đầu tiên.


6

Mã cho dễ đọc. Nếu bạn muốn biết những gì hoạt động tốt hơn, hãy sử dụng một trình lược tả, vì tối ưu hóa và trình biên dịch khác nhau, và các vấn đề về hiệu suất hiếm khi mọi người nghĩ rằng chúng là.


6

Sử dụng chuyển đổi, đó là những gì nó làm và những gì lập trình viên mong đợi.

Mặc dù vậy, tôi sẽ đặt các nhãn trường hợp dư thừa - chỉ để làm cho mọi người cảm thấy thoải mái, tôi đã cố gắng nhớ khi nào / những quy tắc nào để loại bỏ chúng.
Bạn không muốn lập trình viên tiếp theo làm việc với nó phải thực hiện bất kỳ suy nghĩ không cần thiết nào về chi tiết ngôn ngữ (có thể là bạn sau vài tháng nữa!)


4

Trình biên dịch thực sự tốt trong việc tối ưu hóa switch. Gcc gần đây cũng tốt trong việc tối ưu hóa một loạt các điều kiện trong một if.

Tôi đã thực hiện một số trường hợp thử nghiệm trên godbolt .

Khi các casegiá trị được nhóm lại gần nhau, gcc, clang và icc đều đủ thông minh để sử dụng bitmap để kiểm tra xem giá trị có phải là một trong những giá trị đặc biệt hay không.

ví dụ: gcc 5.2 -O3 biên dịch switchthành (và ifmột cái gì đó rất giống nhau):

errhandler_switch(errtype):  # gcc 5.2 -O3
    cmpl    $32, %edi
    ja  .L5
    movabsq $4301325442, %rax   # highest set bit is bit 32 (the 33rd bit)
    btq %rdi, %rax
    jc  .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

Lưu ý rằng bitmap là dữ liệu ngay lập tức, do đó, không có bộ nhớ cache dữ liệu tiềm năng nào truy cập vào nó hoặc bảng nhảy.

gcc 4.9.2 -O3 biên dịch thành switchbitmap, nhưng thực hiện 1U<<errNumbervới Mov / shift. Nó biên dịch ifphiên bản thành một loạt các chi nhánh.

errhandler_switch(errtype):  # gcc 4.9.2 -O3
    leal    -1(%rdi), %ecx
    cmpl    $31, %ecx    # cmpl $32, %edi  wouldn't have to wait an extra cycle for lea's output.
              # However, register read ports are limited on pre-SnB Intel
    ja  .L5
    movl    $1, %eax
    salq    %cl, %rax   # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
    testl   $2150662721, %eax
    jne .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

Lưu ý cách nó trừ 1 từ errNumber(với leađể kết hợp thao tác đó với di chuyển). Điều đó cho phép nó phù hợp với bitmap thành 32 bit ngay lập tức, tránh ngay lập tức 64 bitmovabsq cần nhiều byte lệnh hơn.

Một chuỗi ngắn hơn (trong mã máy) sẽ là:

    cmpl    $32, %edi
    ja  .L5
    mov     $2150662721, %eax
    dec     %edi   # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
    bt     %edi, %eax
    jc  fire_special_event
.L5:
    ret

(Lỗi không sử dụng jc fire_special_eventlà ở khắp mọi nơi và là lỗi của trình biên dịch .)

rep retđược sử dụng trong các mục tiêu nhánh và theo các nhánh có điều kiện, vì lợi ích của AMD K8 và K10 cũ (tiền Bulldozer): `rep ret` nghĩa là gì? . Không có nó, dự đoán nhánh không hoạt động tốt trên các CPU lỗi thời đó.

bt(kiểm tra bit) với một thanh ghi arg là nhanh. Nó kết hợp công việc dịch chuyển trái 1 errNumberbit và thực hiện một test, nhưng vẫn là độ trễ 1 chu kỳ và chỉ một uop Intel duy nhất. Nó chậm với một bộ nhớ arg vì ngữ nghĩa quá giống CISC của nó: với toán hạng bộ nhớ cho "chuỗi bit", địa chỉ của byte được kiểm tra được tính dựa trên đối số khác (chia cho 8) và không không giới hạn ở đoạn 1, 2, 4 hoặc 8byte được chỉ ra bởi toán hạng bộ nhớ.

Từ các bảng hướng dẫn của Agner Fog, hướng dẫn thay đổi số đếm chậm hơn so với btIntel gần đây (2 u thay vì 1 và shift không làm mọi thứ khác cần thiết).


4

Ưu điểm của chuyển đổi () so với trường hợp khác là: - 1. Chuyển đổi hiệu quả hơn nhiều so với trường hợp khác vì mỗi trường hợp không phụ thuộc vào trường hợp trước đó không giống như trường hợp khác khi câu lệnh riêng lẻ cần được kiểm tra cho điều kiện đúng hoặc sai.

  1. Khi có không. trong các giá trị cho một biểu thức, trường hợp chuyển đổi sẽ linh hoạt hơn nếu khác bởi vì trong trường hợp khác phán đoán chỉ dựa trên hai giá trị là đúng hoặc sai.

  2. Các giá trị trong chuyển đổi được người dùng xác định trong khi các giá trị trong trường hợp khác dựa trên các ràng buộc.

  3. Trong trường hợp có lỗi, các câu lệnh trong switch có thể dễ dàng được kiểm tra và sửa chữa, điều này tương đối khó kiểm tra trong trường hợp nếu câu lệnh khác.

  4. Switch case nhỏ gọn hơn nhiều và dễ đọc và dễ hiểu.


2

IMO đây là một ví dụ hoàn hảo về việc chuyển đổi được thực hiện cho.


trong c # đây là trường hợp duy nhất mà suy nghĩ mùa thu xảy ra. Lập luận tốt ngay đó.
BCS

2

Nếu các trường hợp của bạn có khả năng vẫn được nhóm lại trong tương lai - nếu có nhiều hơn một trường hợp tương ứng với một kết quả - thì công tắc có thể chứng minh là dễ đọc và duy trì hơn.


2

Họ làm việc tốt như nhau. Hiệu suất là về cùng một trình biên dịch hiện đại.

Tôi thích nếu các câu lệnh hơn các câu lệnh tình huống vì chúng dễ đọc hơn và linh hoạt hơn - bạn có thể thêm các điều kiện khác không dựa trên đẳng thức số, như "|| max <min". Nhưng đối với trường hợp đơn giản mà bạn đăng ở đây, điều đó không thực sự quan trọng, chỉ cần làm những gì dễ đọc nhất đối với bạn.


2

chuyển đổi chắc chắn được ưa thích. Dễ dàng hơn để xem danh sách các trường hợp chuyển đổi và biết chắc chắn nó đang làm gì hơn là đọc điều kiện dài.

Sự trùng lặp trong ifđiều kiện là khó khăn trên mắt. Giả sử một trong những ==đã được viết!= ; bạn có để ý không Hoặc nếu một ví dụ của 'numError' được viết 'nmuError', điều này vừa xảy ra để biên dịch?

Nói chung, tôi thích sử dụng đa hình thay vì chuyển đổi, nhưng không có thêm chi tiết về bối cảnh, thật khó để nói.

Đối với hiệu suất, cách tốt nhất của bạn là sử dụng một trình lược tả để đo hiệu suất của ứng dụng của bạn trong các điều kiện tương tự như những gì bạn mong đợi trong tự nhiên. Nếu không, có lẽ bạn đang tối ưu hóa ở sai vị trí và sai cách.


2

Tôi đồng ý với tính hiệu quả của giải pháp chuyển đổi nhưng IMO bạn đang chiếm quyền điều khiển ở đây.
Mục đích của công tắc là có cách xử lý khác nhau tùy thuộc vào giá trị.
Nếu bạn phải giải thích thuật toán của mình bằng mã giả, bạn sẽ sử dụng if bởi vì, về mặt ngữ nghĩa, đó là: if anything_error làm điều này ...
Vì vậy, trừ khi bạn có ý định thay đổi mã của mình để có mã cụ thể cho từng lỗi , Tôi sẽ sử dụng nếu .


2
Tôi không đồng ý, vì lý do tương tự mà tôi không đồng ý với trường hợp mặc dù mùa thu. Tôi đọc công tắc là "Trong các trường hợp 01,07,0A, 10,15,16 và 20 sự kiện đặc biệt hỏa hoạn." Không có sự sụp đổ nào cho phần khác., Đây chỉ là một tạo tác của cú pháp C ++ khi bạn lặp lại từ khóa 'trường hợp' cho mỗi giá trị.
MSalters

1

Tôi sẽ chọn tuyên bố if vì mục đích rõ ràng và quy ước, mặc dù tôi chắc chắn rằng một số người sẽ không đồng ý. Rốt cuộc, bạn đang muốn làm một cái gì đó ifđiều kiện là đúng! Có một chuyển đổi với một hành động có vẻ hơi ... không cần thiết.


1

Tôi sẽ nói sử dụng SWITCH. Bằng cách này, bạn chỉ phải thực hiện các kết quả khác nhau. Mười trường hợp giống hệt nhau của bạn có thể sử dụng mặc định. Nếu một thay đổi tất cả những gì bạn cần là thực hiện rõ ràng thay đổi, không cần chỉnh sửa mặc định. Việc thêm hoặc xóa các trường hợp khỏi SWITCH cũng dễ dàng hơn nhiều so với chỉnh sửa IF và ELSEIF.

switch(numerror){
    ERROR_20 : { fire_special_event(); } break;
    default : { null; } break;
}

Thậm chí có thể kiểm tra tình trạng của bạn (trong trường hợp này là số) trong danh sách các khả năng, một mảng có lẽ vì vậy SWITCH của bạn thậm chí không được sử dụng trừ khi chắc chắn sẽ có kết quả.


Có khoảng 30 lỗi tổng cộng. 10 yêu cầu hành động đặc biệt, vì vậy tôi đang sử dụng mặc định cho ~ 20 lỗi không yêu cầu hành động ...
Zing-

1

Thấy rằng bạn chỉ có 30 mã lỗi, mã hóa bảng nhảy của riêng bạn, sau đó bạn tự thực hiện tất cả các lựa chọn tối ưu hóa (nhảy sẽ luôn nhanh nhất), thay vì hy vọng trình biên dịch sẽ làm đúng. Nó cũng làm cho mã rất nhỏ (ngoài khai báo tĩnh của bảng nhảy). Nó cũng có lợi ích phụ là với trình gỡ lỗi, bạn có thể sửa đổi hành vi trong thời gian chạy nếu cần, chỉ bằng cách chọc trực tiếp dữ liệu bảng.


Wow, đó có vẻ như là một cách để biến một vấn đề đơn giản thành một vấn đề phức tạp. Tại sao đi đến tất cả những rắc rối khi trình biên dịch sẽ làm một công việc tuyệt vời cho bạn. Thêm vào đó, nó rõ ràng là một trình xử lý lỗi, vì vậy nó không có khả năng quá nghiêm trọng. Một chuyển đổi là điều dễ dàng nhất để đọc và duy trì.
MrZebra

Một bảng hầu như không phức tạp - thực tế có lẽ nó đơn giản hơn việc chuyển sang mã. Và tuyên bố đã đề cập đến hiệu suất là một yếu tố.
Greg Whitfield

Nghe có vẻ như tối ưu hóa sớm. Miễn là bạn giữ các giá trị enum của bạn nhỏ và liền kề, trình biên dịch sẽ làm điều đó cho bạn. Đặt công tắc trong một chức năng riêng biệt sẽ giữ cho mã sử dụng nó tốt và nhỏ, như Mark Ransom gợi ý trong câu trả lời của anh ta, mang lại lợi ích tương tự cho mã nhỏ.
Peter Cordes

Ngoài ra, nếu bạn đang đi để thực hiện bất cứ điều gì cho mình, làm một std::bitset<MAXERR> specialerror;, sau đó if (specialerror[err]) { special_handler(); }. Điều này sẽ nhanh hơn một bảng nhảy, đặc biệt. trong trường hợp không thực hiện.
Peter Cordes

1

Tôi không chắc chắn về cách thực hành tốt nhất, nhưng tôi sẽ sử dụng chuyển đổi - và sau đó bẫy thông qua cố ý thông qua 'mặc định'


1

Về mặt thẩm mỹ tôi có xu hướng ủng hộ phương pháp này.

unsigned int special_events[] = {
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20
 };
 int special_events_length = sizeof (special_events) / sizeof (unsigned int);

 void process_event(unsigned int numError) {
     for (int i = 0; i < special_events_length; i++) {
         if (numError == special_events[i]) {
             fire_special_event();
             break;
          }
     }
  }

Làm cho dữ liệu thông minh hơn một chút để chúng ta có thể làm cho logic trở nên nhỏ hơn.

Tôi nhận ra nó trông thật kỳ lạ. Đây là nguồn cảm hứng (từ cách tôi làm điều đó trong Python):

special_events = [
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20,
    ]
def process_event(numError):
    if numError in special_events:
         fire_special_event()

4
Cú pháp của một ngôn ngữ không có ảnh hưởng đến cách chúng ta thực hiện một giải pháp ... => Có vẻ xấu xí trong C và thoải mái tại Python. :)
rlerallut

Sử dụng bitmap? Nếu error_0a là 0x0a, v.v. bạn có thể đặt chúng dưới dạng bit trong một thời gian dài. dài đặc biệt_events = 1LL << 1 | 1LL << 7 | 1LL << 0xa ... Sau đó, sử dụng if (Special_events & (1LL << numError) fire_special_event ()
paperhorse

1
Kinh quá. Bạn đã biến một hoạt động trong trường hợp xấu nhất O (1) (nếu các bảng nhảy được tạo ra) thành trường hợp xấu nhất O (N) (trong đó N là số lượng các trường hợp được xử lý) bạn đã sử dụng breakbên ngoài một case(có, một trẻ vị thành niên tội lỗi, nhưng dù sao cũng là một tội lỗi). :)
Mac

Kinh quá? Ông nói hiệu suất và không gian không quan trọng. Tôi chỉ đơn giản là đề xuất một cách khác để xem xét vấn đề. Nếu chúng ta có thể đại diện cho một vấn đề theo cách con người suy nghĩ ít hơn, thì tôi thường không quan tâm nếu điều đó có nghĩa là máy tính phải suy nghĩ nhiều hơn.
mbac32768

1
while (true) != while (loop)

Có lẽ cái đầu tiên được tối ưu hóa bởi trình biên dịch, điều đó sẽ giải thích tại sao vòng lặp thứ hai chậm hơn khi tăng số vòng lặp.


Đây dường như là một bình luận cho câu trả lời của McAnix. Đó chỉ là một trong những vấn đề với nỗ lực về thời gian ifso với switchđiều kiện kết thúc vòng lặp trong Java.
Peter Cordes

1

Khi nói đến việc biên dịch chương trình, tôi không biết có sự khác biệt nào không. Nhưng đối với bản thân chương trình và giữ mã đơn giản nhất có thể, cá nhân tôi nghĩ rằng nó phụ thuộc vào những gì bạn muốn làm. nếu khác nếu các tuyên bố khác có lợi thế của chúng, mà tôi nghĩ là:

cho phép bạn kiểm tra một biến đối với các phạm vi cụ thể mà bạn có thể sử dụng các hàm (Thư viện chuẩn hoặc Cá nhân) làm điều kiện.

(thí dụ:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 if( a > 0 && a < 5)
   {
     cout<<"a is between 0, 5\n";

   }else if(a > 5 && a < 10)

     cout<<"a is between 5,10\n";

   }else{

       "a is not an integer, or is not in range 0,10\n";

Tuy nhiên, nếu khác nếu các tuyên bố khác có thể trở nên phức tạp và lộn xộn (mặc dù nỗ lực tốt nhất của bạn) vội vàng. Các câu lệnh chuyển đổi có xu hướng rõ ràng hơn, sạch hơn và dễ đọc hơn; nhưng chỉ có thể được sử dụng để kiểm tra các giá trị cụ thể (ví dụ:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 switch(a)
 {
    case 0:
    case 1:
    case 2: 
    case 3:
    case 4:
    case 5:
        cout<<"a is between 0,5 and equals: "<<a<<"\n";
        break;
    //other case statements
    default:
        cout<<"a is not between the range or is not a good value\n"
        break;

Tôi thích nếu - khác nếu - khác tuyên bố, nhưng nó thực sự là tùy thuộc vào bạn. Nếu bạn muốn sử dụng các hàm làm điều kiện hoặc bạn muốn kiểm tra một cái gì đó dựa trên một phạm vi, mảng hoặc vectơ và / hoặc bạn không bận tâm đến việc lồng ghép phức tạp, tôi khuyên bạn nên sử dụng If if if if if. Nếu bạn muốn kiểm tra các giá trị đơn lẻ hoặc bạn muốn một khối rõ ràng và dễ đọc, tôi khuyên bạn nên sử dụng các khối trường hợp switch ().


0

Tôi không phải là người để cho bạn biết về tốc độ và việc sử dụng bộ nhớ, nhưng nhìn vào một thống kê chuyển đổi là một địa ngục dễ hiểu hơn rất nhiều sau đó là một tuyên bố if lớn (đặc biệt là 2-3 tháng xuống dòng)


0

Tôi biết nó cũ nhưng

public class SwitchTest {
static final int max = 100000;

public static void main(String[] args) {

int counter1 = 0;
long start1 = 0l;
long total1 = 0l;

int counter2 = 0;
long start2 = 0l;
long total2 = 0l;
boolean loop = true;

start1 = System.currentTimeMillis();
while (true) {
  if (counter1 == max) {
    break;
  } else {
    counter1++;
  }
}
total1 = System.currentTimeMillis() - start1;

start2 = System.currentTimeMillis();
while (loop) {
  switch (counter2) {
    case max:
      loop = false;
      break;
    default:
      counter2++;
  }
}
total2 = System.currentTimeMillis() - start2;

System.out.println("While if/else: " + total1 + "ms");
System.out.println("Switch: " + total2 + "ms");
System.out.println("Max Loops: " + max);

System.exit(0);
}
}

Thay đổi số lượng vòng lặp thay đổi rất nhiều:

Trong khi if / khác: 5ms Chuyển đổi: 1ms Vòng lặp tối đa: 100000

Trong khi if / khác: 5ms Chuyển đổi: 3ms Vòng lặp tối đa: 1000000

Trong khi if / khác: 5ms Chuyển đổi: 14ms Vòng lặp tối đa: 10000000

Trong khi if / khác: 5ms Chuyển đổi: 149ms Vòng lặp tối đa: 100000000

(thêm nhiều tuyên bố nếu bạn muốn)


3
Điểm tốt, nhưng sry, anh bạn, bạn đang nói sai ngôn ngữ. Thay đổi ngôn ngữ thay đổi rất nhiều;)
Gabriel Schreiber

2
Các if(max) breakvòng lặp chạy trong thời gian liên tục không phân biệt số lần lặp? Âm thanh như trình biên dịch JIT đủ thông minh để tối ưu hóa vòng lặp đi counter2=max. Và có lẽ nó chậm hơn chuyển đổi nếu cuộc gọi đầu tiên currentTimeMilliscó nhiều chi phí hơn, bởi vì không phải mọi thứ đều được biên dịch JIT? Đặt các vòng theo thứ tự khác có thể sẽ cho kết quả khác nhau.
Peter Cordes
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.