Operasi pesanan

13

pengantar

Ada saatnya di masa kanak-kanak ketika Anda pikir Anda sudah menguasai menambah dan mengalikan, kemudian seseorang datang dan memberi tahu Anda bahwa:

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

dan itu bukan proses yang sederhana atau linier seperti yang diajarkan sebelumnya. Anda belajar bahwa ada sesuatu yang disebut urutan operasi . Ini adalah cara yang sangat penting untuk menjaga tingkat konsistensi dan ekspresi, tanpa tanda kurung menghalangi segalanya.

Alur cerita umum

Suatu hari, Anda terbangun karena kepanikan di jalanan. Sebuah kelompok ekstremis dengan nama " The 2560 " (kependekan dari "Organisasi Melawan Perintah Operasi", dengan twist hex-ish norak) telah menggunakan metode jahat mereka untuk mengambil kendali atas semua senjata nuklir di dunia. Mereka menyandera seluruh planet, dan mereka memiliki tuntutan sederhana: membalik urutan operasi yang diterima atau menghadapi pemberantasan (tanda kurung harus mempertahankan prioritas mereka). Sistem baru ini disebut PSADME (tanda kurung, pengurangan / penambahan, pembagian / perkalian, eksponen), dan ekspresi mengevaluasi dari kanan ke kiri:

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

Hari-hari berlalu, dan transisi sedang berlangsung. Sementara matematikawan dan fisikawan semuanya sibuk menulis ulang persamaan mereka, para ilmuwan komputer dihadapkan pada tugas mengubah mode di mana ekspresi matematika ditafsirkan oleh komputer. Anda termasuk dalam kelompok pemrograman pemberontak rahasia yang bertujuan untuk menyebabkan banyak siksaan bagi penguasa global baru - dan, secara kebetulan, Anda dipilih secara acak oleh The 2560 dan ditugaskan untuk menghasilkan program perhitungan patokan.

Misi Anda

Tulis sebuah program (atau fungsi) yang mengambil ekspresi matematika (numerik) sebagai input, menghitung ekspresi menggunakan PSADME sebagai urutan operasi dan mengeluarkan hasilnya. Ekspresi harus mengevaluasi dari kanan ke kiri, jadi

1-3+4=1-7=-6.

Untuk kesederhanaan, semua angka yang diberikan akan bilangan bulat, dan perhitungan akan menghasilkan hasil bilangan bulat.

Aturan dan penilaian

  • Program harus menerima panjang input hingga 128 karakter - jika bahasa / platform Anda memiliki panjang input maksimum lebih rendah, itu adalah alasan yang dapat diterima.
  • Celah standar dilarang.
  • Kode yang menang akan dipilih pada tanggal 18 November (4 minggu dari tanggal posting ini).
  • Jangan ragu untuk mengirim kode yang tidak dianggap layak golf. Ini tentang kesenangan. Jika Anda memiliki cara yang menarik untuk melakukan ini tetapi tidak bisa golf sendiri (atau berdasarkan metode Anda), Anda tetap dapat mempostingnya.

Seperti biasa, kode yang menang adalah kode dengan jumlah byte paling sedikit, dengan beberapa bonus nilai hiburan:

  • -5 untuk menghindari penggunaan karakter dalam ekspresi yang disediakan: + , - , ( , ) , ^ , * , /
  • -5 untuk membuat perhitungan membutuhkan lebih dari 5 menit (tetapi tidak lebih dari 10 menit) untuk menghitung pada komputer standar, tanpa metode yang jelas (menggunakan jam atau loop yang tidak perlu); Tujuannya adalah untuk meyakinkan penguasa baru bahwa Anda tidak mencoba mengganggu perhitungan malapetaka mereka.
  • - (5 + N) untuk pesan ofensif langsung (panjang N, tidak termasuk spasi putih terkemuka / tertinggal) tentang anggota The 2560 yang akan ditulis dengan jelas di dalam kode Anda, dengan beberapa penjelasan konyol mengapa harus dilakukan sana. Jika dihapus, kode tidak boleh berfungsi dengan benar. Ya, poin gratis untuk nilai hiburan.

Contoh dan penjelasan

[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

Jake
sumber
1 - 3 + 4 = 1 - 7? Kanan ke kiri akan menyarankan demikian, tapi itu menempatkan penambahan di depan pengurangan, bertentangan dengan PSADME, bukan?
LLlAMnYP
1
@ LLlAMnYP Penambahan dan pengurangan berada dalam "grup" yang sama, sama seperti di PEMDAS, jadi itu terjadi dari kanan ke kiri. Sama dengan multiply / bagi. Lebih tepatnya P(SA)(DM)E.
Geobit
2
Pernyataan ini tidak dimaksudkan untuk diproses dari kanan ke kiri - melainkan, operasi dengan presedensi yang sama dievaluasi dari kanan-pertama. Jadi 4/2 = 2, 2-1 = 1, tetapi a / b c = a / (b c) daripada yang biasa (a / b) * c. Saya harap ini jelas.
Jake
Mungkin cara termudah untuk melakukan ini adalah dengan menulis tata bahasa flex / bison atau lex / yacc.
5
Anda harus mengubah akronim menjadi PADME , karena anggota organisasi jahat seperti itu pasti lebih menyukai trilogi Star Wars yang lebih baru daripada aslinya. Ini juga lebih mudah diingat.
mbomb007

Jawaban:

9

Haskell, 134 byte

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

Mendefinisikan ulang operator matematika dengan perbaikan dan prioritas baru. Sekarang:

*Main> 32 / 8 * 3 - 1
2
nimi
sumber
1
Wow. Cuma wow. Apakah ini mungkin dilakukan dalam bahasa lain? +1
ETHproduk
Saya cukup yakin, itu mungkin dalam Mathematica, atau setidaknya pendekatan yang serupa, tetapi dengan cepat menyadari, saya tidak memiliki pengetahuan untuk membuatnya.
LLlAMnYP
1
Saya cukup baru di sini untuk tidak yakin apakah saran berikut biasanya diterima di forum ini. Ini sepenuhnya didasarkan pada kode Anda, tetapi merupakan skrip bash yang menggunakan Perl untuk menghasilkan file Haskell dan meneruskannya ke GHCi. Dengan melakukan itu, saya menyimpan BYTE SELURUH. 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 Sayangnya, kesalahan ketik membuat kode yang dihasilkan tidak memiliki ruang antara digit dan simbol, tetapi masih berjalan dengan baik. Ini berarti kode Anda bisa kehilangan 5 byte, dan mengalahkan 'peningkatan' saya.
Jake
@JArkinstall Untuk apa nilainya, jawaban saya digunakan secara efektif seduntuk menghasilkan dan mengevaluasi kode shell. Mungkin pertanyaan meta yang bagus.
Trauma Digital
Itu benar, dan saya sangat suka pendekatan Anda - namun, menggunakan alat (perl atau sed) untuk menghasilkan file dalam bahasa yang kemudian dibaca ke dalam bahasa lain tampaknya selangkah lebih maju. Saya tidak akan terlalu terkejut jika ada cara untuk menghasilkan kode di atas melalui generator lain (walaupun metode ini tidak jelas bagi saya!), Dan kita akan menemukan diri kita dalam parse-ception. Jika ini dibolehkan, seseorang bahkan dapat menerapkan pendekatan ini pada kode Anda (dan beberapa contoh yang saya lihat dalam beberapa jawaban yang lebih mudah dibaca untuk beberapa tantangan di forum ini).
Jake
2

GNU sed -r dengan ekstensi 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

Tidak terlalu pendek, tetapi menyelesaikan pekerjaan.

sed tidak apa-apa untuk menguraikan prioritas tetapi tidak melakukan aritmatika. Jadi kami menggunakan ekstensi GNU sed exec ke sperintah untuk melakukan outsourcing aritmatika yang diperlukan ke shell.

Untuk saat ini mengasumsikan semua operator, kecuali ^memiliki tepat satu ruang di depan dan belakang.

Hasil tes:

$ 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
$ 
Trauma Digital
sumber
Gambar profil yang bagus. xD
Oliver Ni
1

JavaScript (ES6) 287 300

Sunting Bug yang diperbaiki (hanya salah ketik, 6 seharusnya 4) - Menambahkan penjelasan lengkap di akhir cuplikan

Edit 2 Ditemukan beberapa peningkatan yang bekerja pada tantangan lain

Namun porting lain dari parser yang sama dengan hanya beberapa perbedaan minimal. (bandingkan dengan ini )

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>

edc65
sumber