'Sed' ile ek


40

Matematiksel bir işlem gerçekleştirmeye çalışıyorum sed, ancak değişkenlerimi dizge olarak işlemeye devam ediyor. Girdi bu tür:

$ echo 12 | sed 's/[0-9]*/&+3/'
$ 12+3

Çıktı olarak 15 tane istiyorum. İşlemi yapmam ve matematiksel sonucunu yalnızca bir geçişte değiştirmem gerekiyor, çünkü programı Python arka plan programı olarak çalıştırıyorum ve stdoutdosyaları yeniden yönlendirmek , bu dosyaları açmak, işlemleri gerçekleştirmek, sonucu çıkarmak, sonuçları çıkarmak gibi geçişlerden kaçınmak istiyorum. değiştirme. Bana göre, sedhepsini tek bir çizgide yapmak en iyisi gibi görünüyor.

Hem girdi hem de çıktı gibi çeşitli şekillerde yayınlamaya çalıştım.

$ echo 12 | sed 's/[0-9]*/int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'

ancak sonuç her zaman ikinci alanın bir baskısıydı.


12
"Değişkenlerinizi" dizge olarak görüyor, çünkü hepsi sed'in yaptığı - string manipülasyonu. Tamsayı kavramı yoktur.
Kevin,

2
Neden sedmatematik yapmak istediğinizi çok merak ediyorum
David Oneill

Sadece değişkenleri kolayca değiştirebileceğini düşündüm, çok karmaşık olmadığını fark etmedim!
Luigi Tiburzi,

Yanıtlar:


82

Dürüst olmak gerekirse sed kullanmak istiyorsanız, o zaman gitmenin yolu budur:

s/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/+//g
: minus
s/|-|/-/g
t minus
s/-$//
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back

Giriş:

1+2
100+250
100-250

Çıktı:

3
350
-150

Göreviniz, kabul etmeyi seçerseniz, çarpımı uygulamaktır.


5
Meydan okuma için +1, onu sev! Belki bu Code Golf için bir şey olurdu ;-p
Tatjana Heuser

6
Bazı insanlar da programlamanın matematik olmadığını söylüyor. Bu küçük mücevher hepsini çürütüyor. Şimdiye kadar Base 1'in en iyi kullanımı.
Bruce Ediger

1
Güzel! - @Simon: Ben uygulamak için size meydan tetrasyon : P
AT

16
+1 Bu, yaratıcılığın eşleştiği bir kavram yanılgısının doğurabileceği şeye güzel bir örnektir.
rozcietrzewiacz

5
Harika! Sözdizimini çok iyi anlamıyorum ama sadece bakmak harika! Ben sed sed gitmenin yolunun olmadığına ikna oldum, teşekkür ederim !!!
Luigi Tiburzi,

20

sedburadaki en iyi seçenek değil, doğal olarak aritmetik yapmıyor (bakınız ancak nasıl yapabileceğinizi öğrenmek için bir sayı artırma ). Bunu ile yapabilirsin awk:

$ echo 12 | awk '{print $0+3}'
15

Kullanılacak en iyi kod parçası, girişinizin tam biçimine ve sayısal değilse veya birden fazla sayı içeriyorsa, ne yapmak / ne yapmak istediğinize bağlı olacaktır.

Bunu sadece aşağıdakilerle de yapabilirsiniz bash:

$ echo $(( $(echo 12) + 3 ))

veya exprbenzer şekilde kullanma.


17

Meydan okumayı kabul etmeye çalıştım @Richter, kodunuzun bir bölümünü kullanarak yaptığım şey buydu:

sed 's/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/.*\*$/0/
s/^\*.*/0/
s/*|/*/
: mult
s/\(|*\)\*|/\1<\1*/ 
t mult
s/*//g
s/<//g
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back'

Giriş:

04*3
4*3
40*3
42*32
150*20
1*3
3*1
0*3
3*0

Çıktı: tüm doğru sonuçlar


@SimonRichter beğeneceğinizi umuyoruz !!
Luigi Tiburzi

Cross bu mükemmel cevabı burada yayınladı: codegolf.stackexchange.com/a/39882/11259
Digital Trauma

12

perlçok benzer bir yapıya izin verir sed… bir fark perldaha karmaşık şeyler yapabiliyor olmasıdır ... sedbasit metin alt bölümleri için çok iyidir

 echo 'a12' | perl -pe 's/([0-9]+)/($1+3)/e'  # the trailing /e means evaluate

çıktı

a15

2
parantez yakalamadan da bunu yapabilir:perl -pe 's/[0-9]+/$&+3/e'
glenn jackman


6

Kabul edilen cevabın aşırı karmaşıklığının neden aşağıdakilerden birinin ne istediğini gerçekten anlamadım:

echo 12 | sed 's/[0-9]*/echo \$(( & + 3 ))/e'

veya

echo 12 | sed 's/[0-9]*/expr & + 3/e'

GNU sed gerektirebilir, ancak emin değilim.


Bu bir gnu uzantısıdır.
Kevin

Tamam haklısınız ama cevap ötesine geçiyor, genel bir ekleme yapmıyor, belirli bir tane değil, iki sayıyı besleyebiliyorsunuz ve sonucu alacaksınız
Luigi Tiburzi

@LuigiTiburzi Bunu "x + y" stil girişine genellemek oldukça basittir:echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Dijital Travma

5

Düzenli ifadeleri ve aritmetik işlemleri kesinlikle birleştirmeniz gerekiyorsa, normal ifadenin değiştirme parametresinin bir geri çağırma işlevi olabileceği bir dil seçin.

Perl, Ruby, JavaScript ve Python böyle dillerdir:

bash-4.2$ echo 12 | perl -pe 's/\d+/$&+3/e'
15

bash-4.2$ echo 12 | ruby -pe '$_.sub!(/\d+/){|s|s.to_i+3}'
15

bash-4.2$ echo 12 | js -e 'print(readline().replace(/\d+/,function(s){return parseInt(s)+3}))'
15

bash-4.2$ echo 12 | python -c 'import re;print re.sub("\d+",lambda s:str(int(s.group(0))+3),raw_input())'
15

1

bashAslında bir boruda çalışan başka bir basit çözüm:

 echo 12 | { read num; echo $(( num + 3)); }

1

Bazı bashism ile karıştırırsanız:

echo $(($(echo 12 | sed 's/[0-9]*/&+3/')))

Numarayı bir metinden çıkarmak için:

echo $(($(echo "foo12bar" | sed -r 's/[^0-9]*([0-9]*).*/\1+3/')))

Sed olmadan, sadece bash:

var="foo12bar"
echo $((${var//[^0-9]/}+3))

${var//[^0-9]/}çift basamaklı olmayan her parite yerine geçer ve aritmetik işlem yapar:$((x+3))


2
Orada hiçbir faşizm yok. $((...))POSIX tarafından tanıtıldı (bashism is $[...]). ${var//xxx/x}zsh ve bash tarafından kopyalanan bir kshismdir. sed -rbir GNUism olan
Stéphane Chazelas

0

İşte bir Perl çözümü:

echo 12 | perl -wlpe '$_ += 3'
# Output:  15

Bir dizede karşılaşılan ilk rakam grubunu değiştirmeyi tercih ederseniz, şunları kullanabilirsiniz:

echo I am 12 years old. | perl -wlpe 's/(\d+)/$1 + 3/e'
# Output:  I am 15 years old.

Bir dizedeki tüm basamak kümelerini değiştirmeyi tercih ederseniz , /gdeğiştiriciyi şunun gibi kullanabilirsiniz :

echo They are 11, 12, and 13 years old. | perl -wlpe 's/(\d+)/$1 + 3/eg'
# Output:  They are 14, 15, and 16 years old.

0

Sed ifadesini kullanmak harika olsa da, sınırlamaları vardır. Örneğin aşağıdaki başarısız:

$ echo "1000000000000000000000000000000+1" | sed -e 's/\([0-9]*\)+\([0-9]*\)/expr \1 + \2/e'
expr: 1000000000000000000000000000000: Numerical result out of range

Bu sınırlamayı aşmak için basitçe saf sed'in gücüne yönelmek ve isteğe bağlı uzunluktaki ondalık toplayıcıyı uygulamak:

#! / bin / sed -f

s / + / \ N / g
s / $ / \ n \ n0 /

: DÖNGÜ
s / ^ \ (. * \) \ (. \) \ n \ (. * \) \ (. \) \ n \ (. * \) \ n \ (. \) $ / 0 \ 1 \ n0 \ 3 \ n \ 5 \ n \ 6 \ 2 \ 4 /
h
s /^.*. \ n * \ n. * \ n \ (... \) $ / \ 1 /

# ondalık ondalık toplayıcı modülü
# GİRİŞ: 3 basamak (Taşıma, A, B,)
# ÇIKIŞ: 2 bit (Taşıma, Toplam)
s / $ /;000 = 00001 = 01002 = 02.003 = 03004 = 04005 = 05006 = 06007 = 07008 = 08009 = 09010 = 01011 = 02012 = 03013 = 04.014 = 05.015 = 06.016 = 07017 = 08018 = 09019 = 10020 = 02021 = 03022 = 04023 = 05.024 = 06025 = 07026 = 08027 = 09028 = 10029 = 11030 = 03031 = 04032 = 05033 = 06034 = 07035 = 08036 = 09037 = 10038 = 11039 = 12040 = 04041 = 05042 = 06043 = 07044 = 08045 = 09046 = 10047 = 11048 = 12049 = 13050 = 05051 = 06052 = 07053 = 08054 = 09055 = 10056 = 11057 = 12058 = 13059 = 14060 = 06061 = 07062 = 08063 = 09064 = 10065 = 11066 = 12067 = 13068 = 14069 = 15070 = 07071 = 08072 = 09073 = 10074 = 11075 = 12076 = 13077 = 14078 = 15079 = 16080 = 08081 = 09.082 = 10083 = 11084 = 12085 = 13086 = 14087 = 15088 = 16089 = 17090 = 09.091 = 10092 = 11093 = 12094 = 13095 = 14096 = 15097 = 16098 = 17099 = 18100 = 01101 = 02102 = 03103 = 04104 = 05105 = 06106 = 07107 = 08108 = 09109 = 10110 = 02111 = 03112 = 04113 = 05114 = 06115 = 07116 = 08117 = 09118 = 10119 = 11120 = 03121 = 04122 = 05123 = 06124 = 07125 = 08126 = 09127 = 10128 = 11129 = 12130 = 04131 = 05132 = 06133 = 07134 = 08135 = 09136 = 10137 = 11138 = 12139 = 13140 = 05141 = 06142 = 07143 = 08144 = 09145 = 10146 = 11147 = 12148 = 13149 = 14150 = 06151 = 07152 = 08153 = 09154 = 10155 = 11156 = 12157 = 13158 = 14159 = 15160 = 07161 = 08162 = 09163 = 10164 = 11165 = 12166 = 13167 = 14168 = 15169 = 16170 = 08171 = 09172 = 10173 = 11174 = 12175 = 13176 = 14177 = 15178 = 16179 = 17180 = 09181 = 10182 = 11183 = 12184 = 13185 = 14186 = 15187 = 16188 = 17189 = 18190 = 10191 = 11192 = 12193 = 13194 = 14195 = 15196 = 16197 = 17198 = 18199 = 19 /
s / ^ \ (... \) [^] * [^]. * \ 1 = \ (.. \) * / \ 2 /
'H
g
s / ^ \ (. * \) \ n \ (. * \) \ n \ (. * \) \ n ... \ n \ (. \) \ (. \) $ / \ 1 \ n \ 2 \ n \ 5 \ 3 \ n \ 4 /
/ ^ \ ([0] * \) \ n \ ([0] * \) \ n / {
        s /^.*. \ n * \ n \ (. * \) \ n \ (. \) / \ 2 \ 1 /
        s / ^ 0 \ (. * \) / \ 1 /
        q
}
b LOOP

Çalışma şekli, Carry Bit'in yanı sıra iki giriş hanesi (A ve B) ekleyen ve bir Sum ve Carry biti üreten bir ondalık toplama modülü uygulamaktır. Bu fikir, ikili toplayıcıyı ikili sayılar için aynı olan elektronikten ödünç alınır . Tek yapmamız gereken, toplayıcıyı tüm rakamlar arasında dolaşmak ve isteğe bağlı uzunluk numaraları ekleyebiliriz (hafıza ile sınırlıdır). Aşağıda işlem yapan toplayıcı var:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

Tam olarak aynı şekilde bir ikili (veya başka bir taban) toplayıcı uygulanabilir. Tek yapmanız gereken s/$/;000=00001...verilen taban için uygun ikame modeliyle başlayan çizgiyi değiştirmek . Örneğin: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ isteğe bağlı uzunluktaki ikili toplayıcı için değiştirme modelidir.

Benim github üzerinde belgelenen kodunu sığdırabilirsiniz .

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.