การดำเนินงานของการสั่งซื้อ


13

บทนำ

มีจุดในวัยเด็กเมื่อคุณคิดว่าคุณเชี่ยวชาญในการเพิ่มและทวีคูณแล้วมีบางคนเข้ามาและแจ้งให้คุณทราบว่า:

a * b + c = (a * b) + c! = a * (b + c),

และมันไม่ได้เป็นกระบวนการที่ง่ายหรือเชิงเส้นเหมือนที่คุณเคยสอนมาก่อน คุณได้เรียนรู้ว่ามีสิ่งที่เรียกว่าคำสั่งของการดำเนินงาน นี่เป็นวิธีที่สำคัญมากในการรักษาระดับความมั่นคงและในการแสดงออกโดยไม่ต้องวงเล็บให้ได้รับทุกอย่าง

เรื่องราวทั่วไป

อยู่มาวันหนึ่งคุณตื่นขึ้นมาด้วยเสียงตื่นตระหนกบนถนน กลุ่มหัวรุนแรงภายใต้ชื่อ " The 2560 " (ย่อมาจาก "Organization Against the Order of Operations" โดยมีการบิด hex-ish dorky) ได้ใช้วิธีการที่ชั่วร้ายเพื่อควบคุมอาวุธนิวเคลียร์ทั้งหมดในโลก พวกเขาถือตัวประกันทั่วทั้งโลกและพวกเขามีความต้องการอย่างง่าย ๆ : ย้อนกลับลำดับการปฏิบัติการที่ได้รับการยอมรับหรือการกำจัดใบหน้า (วงเล็บคือการรักษาลำดับความสำคัญของพวกเขา) ระบบใหม่นี้เรียกว่า PSADME (วงเล็บ, การลบ / การเพิ่ม, การหาร / การคูณ, เลขยกกำลัง) และนิพจน์จะประเมินจากขวาไปซ้าย:

a - b - c = a - (b - c) = a + c - b

วันที่ผ่านไปและการเปลี่ยนแปลงอยู่ในระหว่างดำเนินการ ในขณะที่นักคณิตศาสตร์และนักฟิสิกส์กำลังยุ่งอยู่กับการเขียนสมการใหม่ แต่นักวิทยาศาสตร์คอมพิวเตอร์ก็กำลังเผชิญกับงานที่ต้องเปลี่ยนรูปแบบการตีความทางคณิตศาสตร์โดยคอมพิวเตอร์ คุณอยู่ในกลุ่มการเขียนโปรแกรมลับของกบฏซึ่งมีเป้าหมายที่จะทำให้เกิดการทรมานมากสำหรับผู้ครอบครองโลกใหม่ - และโดยบังเอิญคุณจะได้รับการสุ่มเลือกโดย2560และมอบหมายให้สร้างโปรแกรมการคำนวณเกณฑ์มาตรฐาน

ภารกิจของคุณ

เขียนโปรแกรม (หรือฟังก์ชั่น) ที่รับนิพจน์ทางคณิตศาสตร์ (ตัวเลข) เป็นอินพุตคำนวณนิพจน์โดยใช้PSADMEเป็นลำดับของการดำเนินการและแสดงผลลัพธ์ นิพจน์ควรประเมินจากขวาไปซ้ายดังนั้น

13+4=17=6.

เพื่อความง่ายตัวเลขทั้งหมดที่ให้จะเป็นจำนวนเต็มและการคำนวณจะให้ผลลัพธ์ที่เป็นจำนวนเต็ม

กฎและการให้คะแนน

  • โปรแกรมควรยอมรับอินพุตที่มีความยาวสูงสุด 128 อักขระ - หากภาษา / แพลตฟอร์มของคุณมีความยาวอินพุตสูงสุดต่ำกว่านั่นเป็นข้อแก้ตัวที่ยอมรับได้
  • ช่องโหว่มาตรฐานเป็นสิ่งต้องห้าม
  • รหัสที่ชนะจะถูกเลือกในวันที่ 18 พฤศจิกายน (4 สัปดาห์นับจากวันที่โพสต์นี้)
  • โพสต์รหัสที่จะไม่ถือว่าเป็นกอล์ฟที่คุ้มค่า มันเป็นเรื่องสนุก หากคุณมีวิธีที่น่าสนใจในการทำเช่นนี้ แต่ไม่สามารถตีด้วยตัวคุณเอง (หรือโดยวิธีการของคุณ) คุณสามารถโพสต์ได้

ตามปกติรหัสที่ชนะคือรหัสที่มีจำนวนไบต์น้อยที่สุดพร้อมกับโบนัสความบันเทิงบางส่วน:

  • -5เพื่อหลีกเลี่ยงการใช้อักขระใด ๆ ในนิพจน์ที่ให้มา: + , - , ( , ) , ^ , * , /
  • -5สำหรับการคำนวณใช้เวลามากกว่า 5 นาที (แต่ไม่เกิน 10 นาที) ในการคำนวณบนคอมพิวเตอร์มาตรฐานโดยไม่มีวิธีการที่ชัดเจน (ใช้นาฬิกาหรือลูปที่ไม่จำเป็น) จุดมุ่งหมายคือการโน้มน้าวให้เจ้าเหนือหัวใหม่ที่คุณไม่ได้พยายามขัดขวางการคำนวณการลงโทษของพวกเขา
  • - (5 + N)สำหรับข้อความก้าวร้าวโดยตรง (ความยาว N, ไม่รวมช่องว่างนำหน้า / ต่อท้าย) เกี่ยวกับสมาชิก 2560 ที่จะเขียนด้วยสายตาธรรมดาภายในโค้ดของคุณพร้อมคำอธิบายที่น่าขันว่าทำไมมันถึงต้องเป็น ที่นั่น หากถูกลบรหัสจะต้องทำงานไม่ถูกต้อง ใช่คะแนนฟรีสำหรับมูลค่าความบันเทิง

ตัวอย่างและคำอธิบาย

[program] 2 - 2 - 2
2

2 - (2 - 2) = 2

[program] (2 + 2 * 3 + 3) / 3 + 3
4

(4 * 6) / (3 + 3) = 4

[program] 3 + 2 + 1 ^ 3
216

(3 + 2 + 1) ^ 3 = 216

[program] -5^2
25

(-5) ^ 2 = 25

[program] 32 / 8 * 3 - 1
2

32 / (8 * (3 - 1)) = 32/16 = 2


1 - 3 + 4 = 1 - 7? จากขวาไปซ้ายจะแนะนำเช่นนั้น แต่นั่นเป็นการเพิ่มการลบก่อนหน้าตรงกันข้ามกับ PSADME ใช่ไหม
LLlAMnYP

1
@LLlAMnYP การเพิ่มและการลบอยู่ใน "กลุ่ม" เดียวกันเหมือนกับใน PEMDAS ดังนั้นจะเกิดขึ้นจากขวาไปซ้าย เหมือนกันกับทวีคูณ / หาร มันเป็นเช่นP(SA)(DM)Eนั้นมากขึ้น
Geobits

2
คำสั่งไม่ได้ถูกประมวลผลจากขวาไปซ้าย แต่เป็นการดำเนินการที่มีลำดับความสำคัญเท่ากันได้รับการประเมินจากขวาไปก่อน ดังนั้น 4/2 = 2, 2-1 = 1 แต่ a / b c = a / (b c) มากกว่าปกติ (a / b) * c ฉันหวังว่านี่จะช่วยล้างสิ่งต่างๆ
Jake

อาจเป็นวิธีที่ง่ายที่สุดในการทำเช่นนี้คือเขียนไวยากรณ์แบบยืดหยุ่น / วัวกระทิงหรือ lex / yacc

5
คุณควรเปลี่ยนคำย่อมาเป็นPADMEเนื่องจากสมาชิกขององค์กรที่ชั่วร้ายจะชอบไตรภาคเดอร์สตาร์วอร์สที่ใหม่กว่าเดิมอย่างแน่นอน นอกจากนี้ยังง่ายต่อการจดจำ
mbomb007

คำตอบ:


9

Haskell, 134 ไบต์

import qualified Prelude as P
infixl 6 ^
(^)=(P.^)
infixr 8 + 
(+)=(P.+)
infixr 8 - 
(-)=(P.-)
infixr 7 /
(/)=P.div
infixr 7 *
(*)=(P.*)

นิยามใหม่ตัวดำเนินการทางคณิตศาสตร์ด้วยฟิกซ์เจอร์และลำดับความสำคัญใหม่ ขณะนี้:

*Main> 32 / 8 * 3 - 1
2

1
ว้าว. แค่ว้าว เป็นไปได้ไหมในภาษาอื่น? +1
ETHproductions

ฉันค่อนข้างแน่ใจว่ามันเป็นไปได้ใน Mathematica หรืออย่างน้อยก็เป็นวิธีที่คล้ายกัน แต่ตระหนักได้อย่างรวดเร็วว่าฉันขาดความรู้ที่จะทำให้มัน
LLlAMnYP

1
ฉันใหม่พอที่จะไม่แน่ใจว่าข้อเสนอแนะต่อไปนี้เป็นที่ยอมรับในฟอรัมนี้หรือไม่ มันขึ้นอยู่กับรหัสของคุณทั้งหมด แต่เป็นสคริปต์ทุบตีซึ่งใช้ Perl เพื่อสร้างไฟล์ Haskell และส่งไปยัง GHCi ด้วยการทำเช่นนี้ฉันได้ช่วย BYTE ทั้งหมด perl -e'$_="import qualified Prelude as Pl 6^r 8+r 8-r 7*r 7/";s/(. \d(.))/\ninfix\1\n(\2)=(P.\2)/g;s~\./~.div~;print'>a.hs;ghci a.hs น่าเสียดายที่การพิมพ์ผิดทำให้รหัสที่สร้างไม่มีช่องว่างระหว่างตัวเลขและสัญลักษณ์ แต่ก็ยังทำงานได้ดี ซึ่งหมายความว่ารหัสของคุณอาจลดลง 5 ไบต์และทำให้ฉัน 'ดีขึ้น'
Jake

@JArkinstall สำหรับสิ่งที่คุ้มค่าคำตอบของฉันคือการใช้sedเพื่อสร้างและประเมินเชลล์โค้ดอย่างมีประสิทธิภาพ อาจเป็นคำถามเมตาที่ดี
Digital Trauma

นั่นเป็นเรื่องจริงและฉันชอบแนวทางของคุณมาก - อย่างไรก็ตามการใช้เครื่องมือ (perl หรือ sed) เพื่อสร้างไฟล์ในภาษาที่อ่านแล้วในภาษาอื่นดูเหมือนว่าอีกขั้นหนึ่ง ฉันจะไม่แปลกใจมากเกินไปถ้ามีวิธีการสร้างรหัสข้างต้นผ่านตัวสร้างอื่น (แม้ว่าวิธีนี้จะไม่ชัดเจนสำหรับฉัน!) และเราจะพบตัวเองในการแยกวิเคราะห์ หากสิ่งนี้ได้รับอนุญาตเราสามารถใช้วิธีนี้กับรหัสของคุณ (และตัวอย่างบางส่วนที่ฉันเห็นในคำตอบที่อ่านได้ง่ายขึ้นสำหรับความท้าทายบางอย่างในบอร์ดนี้)
Jake

2

GNU sed -r พร้อมส่วนขยาย exec, 398

s@ *\^ *@ ** @
:
s@\((-?[0-9]+)\)@\1@
t
s@(-?[0-9]+ - )+(-?[0-9]+ - -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [-+] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ / )+(-?[0-9]+ / -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [*/] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ \*\* )+(-?[0-9]+ \*\* -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ \*\* -?[0-9]+)(.*)@bash -c 'echo \1$[\3]\4'@e
t

ไม่สั้นโดยเฉพาะ แต่ทำให้งานเสร็จ

sed ใช้ได้สำหรับการแยกวิเคราะห์ลำดับความสำคัญ แต่ไม่ใช้เลขคณิต ดังนั้นเราจึงใช้ GNU sed exec ส่วนขยายไปยังsคำสั่งเพื่อเอาท์ซอร์สเลขคณิตที่จำเป็นให้กับเชลล์

สำหรับตอนนี้ถือว่าผู้ประกอบการทั้งหมดยกเว้น^มีหนึ่งช่องว่างด้านหน้าและด้านหลัง

ผลการทดสอบ:

$ cat psadme.txt 
2 - 2 - 2
(2 + 2 * 3 + 3) / 3 + 3
3 + 2 + 1 ^ 3
-5^2
32 / 8 * 3 - 1
$ sed -rf psadme.sed psadme.txt 
2
4
216
25
2
$ 

ภาพโปรไฟล์ที่ดี xD
Oliver Ni

1

JavaScript (ES6) 287 300

แก้ไขข้อผิดพลาดได้รับการแก้ไข (เพียงพิมพ์ผิด 6 ควรได้ 4) - เพิ่มคำอธิบายที่สมบูรณ์ในตอนท้ายของตัวอย่าง

แก้ไข 2พบว่ามีการปรับปรุงบางอย่างทำงานกับความท้าทายอื่น

อีกหนึ่งพอร์ตของ parser เดียวกันที่มีความแตกต่างเพียงเล็กน้อย (เปรียบเทียบกับสิ่งนี้ )

f=(x,W=[],Q=['('],z=1,h=p=>'+-*/^^))('.indexOf(p)>>1,C=n=>{for(;h(q=Q.pop())<h(n);W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))a=W.pop(b=W.pop());z&&Q.push(q,n)})=>((x+')').replace(/\d+|\S/g,t=>t>'('?t>'('?~h(t)?z&&t=='-'?z=-z:C(t,z=1):(W.push(z*t),z=0):C(t,z=0):(Q.push(t),z=1)),W.pop())

// More readable
U=(x,W=[],Q=['('],z=1,
  h=p=>'+-*/^^))('.indexOf(p)>>1,
  C=n=>{
    for(;h(q=Q.pop())<h(n);
        W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))
      a=W.pop(b=W.pop());
    z&&Q.push(q,n)
  }
)=>(
  (x+')')
  .replace(/\d+|\S/g,t=> 
       t>'('
       ?t>'('
       ?~h(t)
       ?z&&t=='-'?z=-z:C(t,z=1)
       :(W.push(z*t),z=0)
       :C(t,z=0)
       :(Q.push(t),z=1)
  ),
  W.pop()
)

// TEST
console.log=(...x)=>O.innerHTML+=x.join` `+'\n'

console.log(f('1 - 3 + 4')) // -6
console.log(f('2-2-2')) // 2
console.log(f('(2 + 2 * 3 + 3) / 3 + 3')) // 4
console.log(f('3 + 2 + 1 ^ 3')) // 216
console.log(f('-5^2')) // 25
console.log(f('32 / 8 * 3 - 1')) // 2

// Explained
X=(x,W=[],Q=['('],z=1,
  h=p=> // operator priority '+-:1, */:3, ^:5, ):7, (:9. If an operand then -1
     '+-*/^^))('.indexOf(p)>>1,
  C=n=>{ // Evaluate operators present on stack if they have upper priority, then push new operator on stack
    //console.log('Operand '+n)
    while( h(q = Q.pop()) < h(n) ) // pop operator from op stack and compare priority with current
    {
      // Pop operands from stack and push result
      b = W.pop();
      a = W.pop();
      r = q=='^' ? Math.pow(a,b) : eval('a'+q+'b')
      // console.log('Evaluate '+a+q+b+'='+r)
      W.push(r);
    }
    // if z == 0 do nothing, because the current operands are '(' and ')' that must be discarded
    // else Push again the last operator popped and the current one
    z && Q.push(q, n) // 
  }
)=>(
  (x+')')
  .replace(/[\d.]+|\S/g,t=> {
    //console.log('Q:'+Q,'W:'+W,'T:'+t,'U:'+h(t),'Z:'+z), // display status
    if (t == '(') 
    { // open parenthesis
      z = 1
      Q.push(t) // push a operator, its the highest priority
    }
    else if (t == ')')
    { //close parenthesis
      z = 0
      C(t) 
    }
    else if (h(t) < 0)
    { // operand
      W.push(z*t) // push operand with right sign
      z = 0 // set z to 0 to mark that we just pushed an operand, so next '-' (if present) is a binary operator 
    }
    else
    { // operator
      if (z && t=='-') // the minus is an unary operator (the only unary operator allowed is '-', '+' will not work)
        z =-z // change the sign
      else
        z = 1, // no unary minus
        C(t)
    }    
  }),
  W.pop() // return the value at top of operand stack
)
<pre id=O></pre>

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.