Dodanie z „sed”


40

Próbuję wykonać operację matematyczną sed, ale nadal traktuję moje zmienne jako ciągi znaków. Dane wejściowe tego rodzaju:

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

Chciałbym mieć 15 jako wynik. Muszę wykonać operację i zastąpić jej wynik matematyczny tylko jednym przejściem, ponieważ uruchamiam program jako demon w języku Python i chcę uniknąć fragmentów takich jak przekierowywanie stdoutplików, otwieranie tych plików, wykonywanie operacji, wyodrębnianie wyniku, wykonywanie wymiana. sedWydaje mi się, że najlepiej jest wykonywać wszystko w jednym wierszu.

Próbowałem rzutować zarówno dane wejściowe, jak i wyjściowe na różne sposoby

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

ale rezultatem było zawsze wydrukowanie drugiego pola.


12
Traktuje twoje „zmienne” jako ciągi, ponieważ to wszystko robi sed - manipulacja ciągami. Nie ma pojęcia „liczba całkowita”.
Kevin,

2
Jestem bardzo ciekawy, dlaczego chcesz używać sedmatematyki
David Oneill

Pomyślałem, że z łatwością może rzutować zmienne, ale nie zdawałem sobie sprawy, że to takie skomplikowane!
Luigi Tiburzi

Odpowiedzi:


82

Jeśli naprawdę chcesz korzystać z sed, to jest taka droga:

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

Wejście:

1+2
100+250
100-250

Wynik:

3
350
-150

Twoim zadaniem, jeśli zdecydujesz się to zaakceptować, jest wdrożenie mnożenia.


5
+1 za wyzwanie, uwielbiaj je! Może to byłoby coś dla Code Golf ;-p
Tatjana Heuser

6
A niektórzy twierdzą, że programowanie nie jest matematyką. Ten mały klejnot obala ich wszystkich. Najlepsze wykorzystanie Base 1 kiedykolwiek.
Bruce Ediger,

1
Niezłe! - @Simon: Wzywam cię do wprowadzenia tetracji : P
AT

16
+1 To jest piękny przykład tego, co może rodzić nieporozumienie w połączeniu z kreatywnością.
rozcietrzewiacz

1
Mnożenie odbywa się za pomocą sed - i całkiem dobrze skaluje się do ogromnych liczb!
Toby Speight,

20

sednie jest tutaj najlepszą opcją, nie wykonuje natywnej arytmetyki (zobacz Zwiększ liczbę, jak możesz to zrobić). Możesz to zrobić za pomocą awk:

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

Najlepszy fragment kodu będzie zależał od dokładnego formatu danych wejściowych i tego, co chcesz / musisz zrobić, jeśli nie jest on numeryczny lub zawiera więcej niż jedną liczbę itp.

Możesz to zrobić tylko z bash:

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

lub używając exprw podobny sposób.


17

Próbowałem zaakceptować twoje wyzwanie @Richter, oto co zrobiłem używając części twojego kodu:

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'

Wejście:

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

Dane wyjściowe: wszystkie prawidłowe wyniki


@ SimonRichter mam nadzieję, że Ci się spodoba !!
Luigi Tiburzi

Cross opublikował tę genialną odpowiedź tutaj: codegolf.stackexchange.com/a/39882/11259
Digital Trauma

12

perlpozwala na bardzo podobną konstrukcję do sed... jedną różnicą jest to, że perlmożna robić bardziej złożone rzeczy ... sedjest bardzo dobry dla prostych podstacji tekstowych

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

wydajność

a15

2
może to zrobić również bez przechwytywania nawiasów:perl -pe 's/[0-9]+/$&+3/e'
glenn jackman


6

Naprawdę nie rozumiem, dlaczego ekstremalna złożoność zaakceptowanej odpowiedzi, jedno z poniższych, robi to, co chcesz:

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

lub

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

Myślę, że może to wymagać GNU sed, ale nie jestem pewien.


Jest to rozszerzenie GNU.
Kevin

Ok, masz rację, ale odpowiedź wykracza poza to, że implementuje ogólny dodatek, a nie konkretny, możesz podać dowolne dwie liczby i uzyskasz wynik
Luigi Tiburzi

@LuigiTiburzi Ułatwienie uogólnienia do wejścia w stylu „x + y” jest dość proste:echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Cyfrowa trauma

5

Jeśli zdecydowanie musisz połączyć wyrażenia regularne i operacje arytmetyczne, wybierz język, w którym parametr zastępujący wyrażenie regularne może być funkcją zwrotną.

Perl, Ruby, JavaScript i Python to takie języki:

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

Kolejne proste bashrozwiązanie, które faktycznie działa w rurze:

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

1

Jeśli wmieszasz trochę bashizmu:

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

Aby wyodrębnić liczbę z tekstu:

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

Bez sed, po prostu bash:

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

zastępuje każdą cyfrę ${var//[^0-9]/}i wykonuje arytmetykę w podwójnych okrągłych parens:$((x+3))


2
Nie ma w tym bashizmu. $((...))został wprowadzony przez POSIX (bashism is $[...]). ${var//xxx/x}to kshism również skopiowany przez zsh i bash. sed -rjest GNUizmem
Stéphane Chazelas,

0

Oto rozwiązanie Perla:

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

Jeśli wolisz zmienić pierwszy zestaw cyfr napotkany w ciągu, możesz użyć:

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

Jeśli wolisz zmienić wszystkie zestawy cyfr w ciągu, możesz użyć /gmodyfikatora w następujący sposób:

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

Chociaż użycie wyrażenia sed jest świetne, ma swoje ograniczenia. Na przykład nie powiedzie się:

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

Aby przezwyciężyć to ograniczenie, po prostu zwracam się do wbudowanej mocy czystego seda i implementuję następujący sumator dziesiętny o dowolnej długości:

#! / bin / sed -f

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

:PĘTLA
s / ^ \ (. * \) \ (. \) \ n \ (. * \) \ (. \) \ n \ (. * \) \ n \ (. \) $ / 0 \ 1 \ n0 \ 3 \ n \ 5 \ n \ 6 \ 2 \ 4 /
h
s /^.* \ n. * \ n. * \ n \ (... \) $ / \ 1 /

# dziesiętny moduł sumatora pełnego
# WEJŚCIE: 3digits (Carry in, A, B,)
# WYJŚCIE: 2 bity (Carry, Sum)
s / $ /;000 = 00001 = 01002 = 02003 = 03004 = 04005 = 05006 = 06007 = 07008 = 08009 = 09010 = 01011 = 02012 = 03013 = 04014 = 05015 = 06016 = 07017 = 08018 = 09019 = 10020 = 02021 = 03022 = 04023 = 05024 = 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 = 09082 = 10083 = 11084 = 12085 = 13086 = 14087 = 15088 = 16089 = 17090 = 09091 = 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 = 112 12193 = 13194 = 14195 = 15196 = 16197 = 17198 = 18199 = 19 /
s / ^ \ (... \) [^;] *; [^;] * \ 1 = \ (.. \). * / \ 2 /
H.
sol
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 PĘTLA

Działa to poprzez implementację modułu sumatora dziesiętnego, który dodaje dwie cyfry wejściowe (A i B), a także Carry Bit i generuje bit Sum i Carry. Pomysł zapożyczono z wersji elektronicznej, w której sumator binarny robi to samo dla liczb binarnych. Wystarczy, że dodamy pętlę do wszystkich cyfr i możemy dodać dowolne liczby długości (ograniczone pamięcią). Poniżej znajduje się sumator w akcji:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

Dokładnie w ten sam sposób można zaimplementować binarny (lub dowolny inny podstawowy) sumator. Wszystko, co musisz zrobić, to zastąpić linię rozpoczynającą się s/$/;000=00001...od właściwego wzorca podstawienia dla danej bazy. Na przykład: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ jest wzorem podstawiania dla binarnego sumatora o dowolnej długości.

Możesz dopasować kod udokumentowany na moim githubie .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.