Sonek ifadelerini postfix notasyonuna dönüştür


23

Bu kapalı sorunun başlığını gördüğümde ilginç bir kod golf mücadelesine benziyordum. Öyleyse onu şu şekilde sunmama izin verin:

Meydan okuma:

Bir aritmetik ifade verilen bir program, ekspresyonu ya da alt yordam Yazın infix gösterimde gibi 1 + 2, aynı ifadeyi verir sonek gösterimde , yani 1 2 +.

(Not: Ocak ayında daha önce de benzer bir mücadele yayınlanmıştı. Bununla birlikte, bu ayrı meseleyi haklı çıkarmak için iki görevin yeterince farklı olduğunu düşünüyorum. sadece hepsini atmak değil.

Giriş:

Giriş oluşan geçerli bir çatı ve çatı aritmetik ifade oluşur numaraları (negatif olmayan tamsayılardır, bir ya da daha fazla ondalık haneli dizileri olarak ifade edilir), dengeli parantez gruplandırılmış bir alt ifade gösterir ve dört çatı ve çatı ikili operatörler + , -, *ve /. Bunlardan herhangi biri (ve tüm ifadeyi çevreleyen), ihmal edilmesi gereken rastgele sayıda boşluk karakteri ile ayrılabilir. 1

Resmi dilbilgisi sevenler için, geçerli girdileri tanımlayan basit bir BNF benzeri dilbilgisi. Kısalık ve açıklık için, dilbilgisi, iki belirteç arasında (bir sayıdaki rakamlar dışında) oluşabilecek isteğe bağlı alanları içermez:

expression     := number | subexpression | expression operator expression
subexpression  := "(" expression ")"
operator       := "+" | "-" | "*" | "/"
number         := digit | digit number
digit          := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

1 Boşlukların varlığının ayrıştırmayı etkileyebileceği tek durum, iki ardışık sayıyı ayırmalarıdır; bununla birlikte, bir operatör tarafından ayrılmayan iki sayı geçerli bir ek ifadesinde bulunamadığından, bu durum hiçbir zaman geçerli girdide olamaz.

Çıktı:

Çıktı, girişe eşdeğer bir postfix ifadesi olmalıdır. Çıkış ifade ile, sadece sayı ve operatörlerin oluşmalıdır tek bir boşluk karakteri aşağıdaki gramer (ki burada, komşu jeton çifti arasında etmez boşluk) 2 :

expression  := number | expression sp expression sp operator
operator    := "+" | "-" | "*" | "/"
number      := digit | digit number
digit       := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
sp          := " "

2 Yine sadelik için, numberbu gramerdeki üretim, aşağıdaki kurallara göre çıktıda yasaklanmış olsalar bile, önde gelen sıfırlarla sayıları kabul eder.

Operatör Önceliği:

Parantez yokluğunda, aşağıdaki öncelik kuralları geçerlidir:

  • Operatörler *ve /daha yüksek önceliğe sahiptir +ve -.
  • Operatörler *ve /birbirlerine eşit önceliğe sahiptir.
  • Operatörler +ve -birbirlerine eşit önceliğe sahiptir.
  • Tüm operatörler birleşiktir.

Örneğin, aşağıdaki iki ifade eşdeğerdir:

1 + 2 / 3 * 4 - 5 + 6 * 7
((1 + ((2 / 3) * 4)) - 5) + (6 * 7)

ve her ikisi de aşağıdaki çıktıyı vermelidir:

1 2 3 / 4 * + 5 - 6 7 * +

(Bunlar aynı öncelik kuralları C dilinde olduğu gibi ve bundan kaynaklanan birçok dilde bulunmaktadır. Muhtemelen, sen ilkokulda öğretilen kurallarla benzer nispi öncelik için muhtemelen hariç *ve /.)

Çeşitli kurallar:

  • Verilen çözüm bir ifade veya alt yordamsa, giriş sağlanmalı ve çıkış tek bir dize olarak döndürülmelidir. Çözüm tam bir programsa, standart girdiden infix ifadesini içeren bir satırı okumalı ve postfix versiyonunu içeren bir satırı standart çıktıya yazdırmalıdır.

  • Giriş sayılar olabilir gelen sıfırların. Çıktıdaki sayılar, baştaki sıfırlara sahip olmamalıdır (olduğu gibi çıkacak 0 sayısı hariç 0).

  • İfadeyi hiçbir şekilde değerlendirmeniz veya optimize etmeniz beklenmez. Özellikle, operatörlerin zorunlu, her türlü ortak, değişmeli veya diğer cebirsel kimlikleri sağladığını varsaymamalısınız. Yani, 1 + 2eşittir 2 + 1ya da 1 + (2 + 3)eşittir olduğunu varsaymamalısınız (1 + 2) + 3.

  • Girişteki sayıların 2 31 - 1 = 2147483647 değerini aşmadığını varsayabilirsiniz .

Bu kurallar, doğru çıkışın girdi tarafından benzersiz bir şekilde tanımlanmasını sağlamak için tasarlanmıştır.

Örnekler:

İşte bazı geçerli giriş ifadeleri ve formda sunulan ilgili çıkışlar "input" -> "output":

"1"                  ->  "1"
"1 + 2"              ->  "1 2 +"
" 001  +  02 "       ->  "1 2 +"
"(((((1))) + (2)))"  ->  "1 2 +"
"1+2"                ->  "1 2 +"
"1 + 2 + 3"          ->  "1 2 + 3 +"
"1 + (2 + 3)"        ->  "1 2 3 + +"
"1 + 2 * 3"          ->  "1 2 3 * +"
"1 / 2 * 3"          ->  "1 2 / 3 *"
"0102 + 0000"        ->  "102 0 +"
"0-1+(2-3)*4-5*(6-(7+8)/9+10)" -> "0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -"

(En azından, bunların hepsinin doğru olduğunu umuyorum ; dönüşümü elle yaptım, bu yüzden hatalar çarptı.)

Sadece açık olmak gerekirse, aşağıdaki girişlerin tümü geçersiz; o mu değil (tabii ki, örneğin bellek sonsuz miktarda tüketen, diyelim ki, bir hata mesajı daha güzel dönen, rağmen) çözüm onlara verdiği takdirde ne olursa olsun:

""
"x"
"1 2"
"1 + + 2"
"-1"
"3.141592653589793"
"10,000,000,001"
"(1 + 2"
"(1 + 2)) * (3 / (4)"

Lisp, gösterim gibi kabul edilebilir mi? Örneğin, 1 2 3 4 +ortalama 1 + 2 + 3 + 4`.
Hauleth,

3
@Hauleth: Bu zorlukla değil, hayır. Ayrıca parantez olmadan nasıl ayrıştırırsınız 1 2 3 4 + *?
Ilmari Karonen

Öyleyse, otuput'ta takip eden boşluklara (yeni satır dahil) izin verilmiyor?
breadbox

@breadbox: Son satırlar tamam. Aslında, sondaki boşluklara izin verildiğini açıkça açıklamama izin verin.
Ilmari Karonen

Bana göre doğru görünen son geçerli örnek için "0 1 - 2 3 - 4 * 5 6 7 8 + 9 / - 10 + * - +" çıkaran bir çözüme sahibim. Kontrol edebilir misin? (Son + operatöre dikkat edin)
coredump

Yanıtlar:


8

Kabuk yarar - 60 karakter

bc -c|sed -re's/[@iK:Wr]+/ /g;s/[^0-9]/ &/g;s/ +/ /g;s/^ //'

Çeşitli problemler çözüldü, ancak daha uzun sürdü :(


1
Bu akıllıca, 9'dan daha büyük sayıları doğru işlemiyor gibi görünüyor.
breadbox

@breadbox, sed -re's/[:@iKWr]+/ /g'1 karakter maliyeti ile düzeltir.
ugoren

ayy, @ugoren önerisi işe yaramaz, çünkü ardışık operatörler artık aralarında boşluk bırakmaz; Ben de bunun için bir düzeltme yapmalıyım
Geoff Reedy

4

C, 250 245 236 193 185 karakter

char*p,b[99];f(char*s){int t=0;for(;*p-32?
*p>47?printf("%d ",strtol(p,&p,10)):*p==40?f(p++),++p:
t&&s[t]%5==2|*p%5-2?printf("%c ",s[t--]):*p>41?s[++t]=*p++:0:++p;);}
main(){f(p=gets(b));}

İşte hala temel mantığı yansıtan ungolfed kaynağının okunabilir bir versiyonu. Aslında oldukça basit bir program. Yapması gereken tek gerçek iş, yüksek ilişkilendirme işleci ile karşılaşıldığında düşük ilişkilendirme işlecini istifin üzerine itmek ve sonra bu alt ifadenin "sonunda" geri çekmektir.

#include <stdio.h>
#include <stdlib.h>

static char buf[256], stack[256];
static char *p = buf;

static char *fix(char *ops)
{
    int sp = 0;

    for ( ; *p && *p != '\n' && *p != ')' ; ++p) {
        if (*p == ' ') {
            continue;
        } else if (*p >= '0') {
            printf("%ld ", strtol(p, &p, 10));
            --p;
        } else if (*p == '(') {
            ++p;
            fix(ops + sp);
        } else {
            while (sp) {
                if ((ops[sp] == '+' || ops[sp] == '-') &&
                        (*p == '*' || *p == '/')) {
                    break;
                } else {
                    printf("%c ", ops[sp--]);
                }
            }
            ops[++sp] = *p;
        }
    }
    while (sp)
        printf("%c ", ops[sp--]);
    return p;
}

int main(void)
{
    fgets(buf, sizeof buf, stdin);
    fix(stack);
    return 0;
}

Çıkartarak karakterleri kaydedin if. Eg if(!*p||*p==41)return p;s[++t]=*p;}->return*p&&*p-41?s[++t]=*p:p;
ugoren

K&R tarzı beyanı:*f(p,s)char*p,s;{
ugoren

1. ifTest başarısız olursa geri dönmek bir hatadır . 2. Biliyorum, fakat K & R işlevi azalır, çizgiyi çizdiğim yer burasıdır. Sadece onlara geri dönemem.
breadbox

Zaten dönüş fonksiyonun sonunda sanıyordum. Kaçırılan }}ve for. Ama işte bir gelişme:printf(" %ld"+!a,...
ugoren

1
Ayrıca, pküresel olmanız gerektiğini düşünüyorum (özyinelemeli çağrı sadece parayanı arayana geri atar ). Öyleyse yap f(p=gets(b)).
ugoren

2

Bash ve Haskell w / C önişlemcisi sed, 180 195 198 275

echo 'CNumO+O-O*fromInteger=show
CFractionalO/
main=putStr$'$*|sed 's/C\([^O]*\)/instance \1 String where /g
s/O\(.\?\)/a\1b=unwords\[a,b,\"\1\"];/g'|runghc -XFlexibleInstances 2>w

Sonunda, artık C çözümünden daha uzun değil. Önemli Haskell kısmı neredeyse bc çözümü kadar tembel.

Komut satırı parametreleri olarak girdi alır. wBu değişikliği beğenmezseniz, bazı ghc uyarı mesajlarını içeren bir dosya oluşturulur runghc 2>/dev/null.


1
Basked? ( Bas , h + H Aske ll + s ed )
CalculatorFeline

2

Python 2, 290 272 268 250 243 238 bayt

Şimdi nihayet JS cevaptan daha kısa!

Bu, şönt bahçe algoritmasının temel bir uygulamasını kullanan tam bir programdır . Giriş, alıntılanan bir dize olarak verilir ve sonuç olarak yazdırılır STDOUT.

import re
O=[];B=[]
for t in re.findall('\d+|\S',input()):exec("O=[t]+O","i=O.index('(');B+=O[:i];O=O[i+1:]","while O and'('<O[0]and(t in'*/')<=(O[0]in'*/'):B+=O.pop(0)\nO=[t]+O","B+=`int(t)`,")[(t>'/')+(t>')')+(t>'(')]
print' '.join(B+O)

Çevrimiçi deneyin!


Açıklama:

Yapmamız gereken ilk şey, girişi belirteçlere dönüştürmek. Bunu \d+|\S, kabaca "herhangi bir basamak grubuna ve boşluk içermeyen herhangi bir karaktere" çevrilen tüm regex eşleşmelerini bularak yaparız . Bu, boşlukları kaldırır, bitişik basamakları tek belirteçler olarak ayrıştırır ve operatörleri ayrı ayrı ayrıştırır.

Şönt sahası algoritması için kullanmamız gereken 4 farklı simge türü vardır:

  • ( - Sol parantez
  • ) - Sağ parantez
  • +-*/ - Operatörler
  • 9876543210 - Sayısal değişmezler

Neyse ki, bunların ASCII kodlarının tümü gösterilen sıraya göre gruplandırılmıştır; bu nedenle, ifade (t>'/')+(t>')')+(t>'(')türünü hesaplamak için ifadeyi kullanabiliriz . Bu , basamaklar için 3 , operatörler için 2 , sağ parantez için 1 ve sol parantez için 0 ile sonuçlanır .

Bu değerleri kullanarak exec, karşılık gelen parçacığı belirtmek için belirteç türüne bağlı olarak büyük parçayı dizine alırız . Bu, her belirteç için farklıdır ve şant alanı algoritmasının bel kemiğidir. İki liste (yığın olarak) kullanılır: O(işlem yığını) ve B(çıktı tamponu). Tüm belirteçler çalıştırıldıktan sonra, yığtaki kalan operatörler Oçıktı arabelleğiyle birleştirilir ve sonuç yazdırılır.


2

Prolog (SWI-Prolog) , 113 bayt

c(Z,Q):-Z=..[A,B,C],c(B,S),c(C,T),concat_atom([S,T,A],' ',Q);term_to_atom(Z,Q).
p(X,Q):-term_to_atom(Z,X),c(Z,Q).

Çevrimiçi deneyin!

SWI Prolog, bunun için GNU Prolog’dan daha iyi bir yapıya sahip.

açıklama

term_to_atomgeriye doğru çalıştırılırsa, bir ayrıştırma notasyonu ifadesini (bir atom olarak saklanır) bir ayrıştırma ağacına (olağan öncelik kurallarına uyarak ve baştaki sıfırları ve boşlukları silerek) ayrıştırır. Daha sonra c, ayrıştırıcı ağaç üzerinde yapısal bir özyineleme yapmak için yardımcı yüklemi kullanıyoruz, derinlik ilk önce postfix notasyonuna dönüştürüyoruz.


1

Javascript (ES6), 244 bayt

f=(s,o={'+':1,'-':1,'*':2,'/':2},a=[],p='',g=c=>o[l=a.pop()]>=o[c]?g(c,p+=l+' '):a.push(l||'',c))=>(s.match(/[)(+*/-]|\d+/g).map(c=>o[c]?g(c):(c==')'?eval(`for(;(i=a.pop())&&i!='(';)p+=i+' '`):c=='('?a.push(c):p+=+c+' ')),p+a.reverse().join` `)

Örnek:
Ara: f('0-1+(2-3)*4-5*(6-(7+8)/9+10)')
Çıktı: 0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -(sondaki boşlukla)

Açıklama:

f=(s,                                                     //Input string
    o={'+':1,'-':1,'*':2,'/':2},                          //Object used to compare precedence between operators
    a=[],                                                 //Array used to stack operators
    p='',                                                 //String used to store the result
    g=c=>                                                 //Function to manage operator stack
        o[l=a.pop()]>=o[c]?                               //  If the last stacked operator has the same or higher precedence
            g(c,p+=l+' '):                                //  Then adds it to the result and call g(c) again
            a.push(l||'',c)                               //  Else restack the last operator and adds the current one, ends the recursion.
)=>                                                       
    (s.match(/[)(+*/-]|\d+/g)                             //Getting all operands and operators
    .map(c=>                                              //for each operands or operators
        o[c]?                                             //If it's an operator defined in the object o
            g(c)                                          //Then manage the stack
            :(c==')'?                                     //Else if it's a closing parenthese
                eval(`                                    //Then
                    for(;(i=a.pop())&&i!='(';)            //  Until it's an opening parenthese
                        p+=i+' '                          //  Adds the last operator to the result
                `)                                        
                :c=='('?                                  //Else if it's an opening parenthese
                    a.push(c)                             //Then push it on the stack
                    :p+=+c+' '                            //Else it's an operand: adds it to the result (+c removes the leading 0s)
        )                                                 
    )                                                     
    ,p+a.reverse().join` `)                               //Adds the last operators on the stack to get the final result

1

R, 142 bayt

R, tekerleği yeniden icat etmek yerine, sadece ayrıştırıcıyı çalıştıran, önek notasyonu çıkaran ve postfix notasyonuna geçmek için özyinelemeli bir fonksiyon kullanan ayrıştırıcıyı kullanabiliyoruz.

f=function(x,p=1){
if(p)x=match.call()[[2]]
if((l=length(x))>1){
f(x[[2]],0)
if(l>2)f(x[[3]],0)
if((z=x[[1]])!="(")cat(z,"")
}else cat(x,"")
}

pArgüman standart dışı değerlendirmede (her yerde R programcılar bane) kullanımını kontrol etmek ve birkaç ekstra vardırif (biz kaçınmak istiyoruz) parantez çıktısı kontrol etmek Orada s.

Giriş: (0-1+(2-3)*4-5*(6-(7+8)/9+10))

Çıktı: 0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -

Giriş: (((((1))) + (2)))

Çıktı: 1 2 +

Bir bonus olarak, rastgele sembollerle ve en fazla iki argüman içeren önceden tanımlanmış fonksiyonlarla çalışır:

Euler kimliği

Giriş: e^(i*pi)-1

Çıktı: e i pi * ^ 1 -

13 ile 1 arasında 100 temettü

Giriş: which(1:100 %% 13 == 0)

Çıktı: 1 100 : 13 %% 0 == which

Yavru tavuk ağırlığının zamanın bir fonksiyonu olarak doğrusal gerilemesi

Giriş: summary(lm(weight~Time, data=ChickWeight))

Çıktı: weight Time ~ ChickWeight lm summary

Son örnek OP'nin kapsamının biraz dışında olabilir, ancak postfix notasyonu kullanıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.