0.02 वेतन वृद्धि के साथ बैश लूप


11

मैं कोशिश कर रहा वेतन वृद्धि के रूप में 0.02 के साथ बैश में लूप बनाना चाहता हूं

for ((i=4.00;i<5.42;i+=0.02))
do
commands
done

लेकिन यह काम नहीं किया।


9
बैश फ्लोटिंग पॉइंट मैथ नहीं करता है।
जोर्डनम

1
वेतन वृद्धि द्वारा किया जा सकता है bc, लेकिन 4.52 पर रोक मुश्किल हो सकता है। @roaima सुझाव का उपयोग करें, 2 के चरण के साथ सहायक संस्करण है, और उपयोग करेंi=$(echo $tmp_var / 100 | bc)
आर्केमर


5
आप आमतौर पर फ्लोट्स का उपयोग लूप इंडेक्स के रूप में नहीं करना चाहते हैं । आप प्रत्येक पुनरावृत्ति पर त्रुटि जमा कर रहे हैं।
isanae

जवाबों:


18

bash मैन पेज पढ़ना निम्नलिखित जानकारी देता है:

for (( expr1 ; expr2 ; expr3 )) ; do list ; done

सबसे पहले, अंकगणितीय अभिव्यक्ति expr1का मूल्यांकन नीचे दिए गए नियमों के अनुसार किया गया है। [...]

और तब हमें यह अनुभाग मिलता है

सशस्त्र विकास

शेल अंकगणितीय अभिव्यक्तियों का मूल्यांकन करने की अनुमति देता है, कुछ परिस्थितियों में (देखें letऔर declareअंतर्निहित आदेश और अंकगणितीय विस्तार)। अति-प्रवाह के लिए बिना किसी जाँच के निश्चित-चौड़ाई वाले पूर्णांकों में मूल्यांकन किया जाता है [...]

तो यह स्पष्ट रूप से देखा जा सकता है कि आप forगैर-पूर्णांक मानों के साथ एक लूप का उपयोग नहीं कर सकते हैं।

एक समाधान बस अपने सभी लूप घटकों को 100 से गुणा करने के लिए हो सकता है, इसके लिए अनुमति देता है जहां आप बाद में उनका उपयोग करते हैं, जैसे:

for ((k=400;k<542;k+=2))
do
    i=$(bc <<<"scale=2; $k / 100" )    # when k=402 you get i=4.02, etc.
    ...
done

मुझे लगता है कि यह k=400;k<542;k+=2इसके साथ सबसे अच्छा समाधान है जो संभावित अस्थायी बिंदु अंकगणितीय परेशानियों से भी बचा जाता है।
ह्यूजेंस

1
ध्यान दें कि लूप में प्रत्येक पुनरावृत्ति के लिए, आप एक पाइप बनाते हैं (आउटपुट को पढ़ने के लिए bc), एक प्रक्रिया को कांटा, एक अस्थायी फ़ाइल (यहां-स्ट्रिंग के लिए) बनाएं, इसमें निष्पादित करें bc(जिसका अर्थ है एक निष्पादन योग्य और साझा लाइब्रेरी लोड करना और उन्हें आरंभ करना), इसके लिए प्रतीक्षा करें और सफाई करें। bcलूप करने के लिए एक बार दौड़ना बहुत अधिक कुशल होगा।
स्टीफन चेजलस

@ स्टीफनचेज़ेलस हाँ, सहमत हैं। लेकिन अगर यह अड़चन है तो हम शायद गलत भाषा में कोड लिख रहे हैं। OOI जो कम अकुशल (!) है। i=$(bc <<< "scale...")याi=$(echo "scale..." | bc)
रोज़ा

1
मेरे त्वरित परीक्षण से, पाइप संस्करण तेजी से zsh (जहां से <<<आता है) में है, bashऔर ksh। ध्यान दें कि किसी अन्य शेल पर स्विच करने से bashआपको अन्य सिंटैक्स का उपयोग करने की तुलना में बेहतर प्रदर्शन को बढ़ावा मिलेगा।
स्टीफन चेजलस

(और गोले के सबसे समर्थन है कि <<<(zsh, mksh, ksh93, यश) भी चल बिन्दु arithmetics (समर्थन zsh, ksh93, yash))।
स्टीफन चेजलस

18

गोले में छोरों से बचें।

यदि आप अंकगणित करना चाहते हैं, तो उपयोग करें awkया bc:

awk '
  BEGIN{
    for (i = 4.00; i < 5.42; i+ = 0.02)
      print i
  }'

या

bc << EOF
for (i = 4.00; i < 5.42; i += 0.02)  i
EOF

ध्यान दें कि awk(इसके विपरीत bc) आपके प्रोसेसर doubleफ्लोटिंग पॉइंट नंबर प्रतिनिधित्व के साथ काम करता है (संभवतः IEEE 754 प्रकार)। परिणामस्वरूप, चूंकि वे संख्याएँ उन दशमलव संख्याओं के द्विआधारी सन्निकटन हैं, तो आपको कुछ आश्चर्य हो सकता है:

$ gawk 'BEGIN{for (i=0; i<=0.3; i+=0.1) print i}'
0
0.1
0.2

यदि आप जोड़ते हैं तो आप OFMT="%.17g"गुम होने का कारण देख सकते हैं 0.3:

$ gawk 'BEGIN{OFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i}'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5

bc इस तरह की समस्या नहीं है तो मनमानी परिशुद्धता करता है।

ध्यान दें कि (जब तक आप आउटपुट फॉर्मेट को संशोधित OFMTया printfस्पष्ट प्रारूप विनिर्देशों के साथ उपयोग नहीं करते हैं), फ़्लोटिंग पॉइंट नंबर प्रदर्शित करने के लिए awkउपयोग करता है %.6g, इसलिए 1e6 और इसके बाद के संस्करण 1,000,000 से ऊपर के फ़्लोटिंग पॉइंट नंबरों के लिए स्विच करेगा और उच्च संख्याओं के लिए आंशिक भाग को काट देगा (100000.02) 100000 के रूप में प्रदर्शित किया जाएगा)।

यदि आपको वास्तव में शेल लूप का उपयोग करने की आवश्यकता है, क्योंकि उदाहरण के लिए आप उस लूप के प्रत्येक पुनरावृत्ति के लिए विशिष्ट कमांड चलाना चाहते हैं, या तो फ़्लोटिंग पॉइंट अंकगणितीय समर्थन जैसे शेल का उपयोग करें zsh, yashया ksh93ऊपर दिए गए एक कमांड के साथ मानों की सूची उत्पन्न करें। (या seqयदि उपलब्ध हो) और इसके आउटपुट पर लूप।

पसंद:

unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
  something with "$i"
done

या:

seq 4 0.02 5.42 | while IFS= read i; do
  something with "$i"
done

जब तक आप अपने प्रोसेसर फ़्लोटिंग पॉइंट नंबरों की सीमा को आगे नहीं seqबढ़ाते हैं , फ़्लोटिंग पॉइंट सन्निकटन द्वारा की गई त्रुटियों को awkउपरोक्त संस्करण की तुलना में अधिक सुंदर तरीके से संभालता है ।

यदि आपके पास seq(GNU कमांड) नहीं है, तो आप एक फ़ंक्शन के रूप में अधिक विश्वसनीय बना सकते हैं:

seq() { # args: first increment last
  bc << EOF
    for (i = $1; i <= $3; i += $2) i
EOF
}

जो कि चीजों के लिए बेहतर काम करेगा seq 100000000001 0.000000001 100000000001.000000005। हालाँकि, ध्यान दें कि मनमाने ढंग से उच्च परिशुद्धता के साथ संख्या होने पर बहुत मदद नहीं मिलेगी अगर हम उन्हें उन आदेशों को पारित करने जा रहे हैं जो उनका समर्थन नहीं करते हैं।


मैं जाग के उपयोग की सराहना करता हूं! +1
पंड्या

आपको unset IFSपहले उदाहरण की आवश्यकता क्यों है ?
user1717828

@ user1717828, आदर्श रूप से, उस विभाजन + ग्लोब मंगलाचरण के साथ, हम newline वर्णों पर विभाजन करना चाहते हैं। हम ऐसा कर सकते हैं, IFS=$'\n'लेकिन यह सभी गोले में काम नहीं करता है। या IFS='<a-litteral-newline-here>'लेकिन यह बहुत सुपाठ्य नहीं है। या हम इसके बजाय शब्दों पर विभाजित कर सकते हैं (स्पेस, टैब, न्यूलाइन) जैसे आप $ IFS के डिफ़ॉल्ट मूल्य के साथ प्राप्त करते हैं या यदि आप IFS को परेशान करते हैं और यहां काम भी करते हैं।
स्टीफन चेज़लस

@ user1717828: हमें इसके साथ खिलवाड़ करने की आवश्यकता नहीं है IFS, क्योंकि हम जानते हैं कि seqआउटपुट में वे स्थान नहीं हैं जिन पर विभाजन से बचने की आवश्यकता है। यह ज्यादातर यह सुनिश्चित करने के लिए है कि आप यह महसूस करते हैं कि यह उदाहरण निर्भर करता है IFS, जो एक अलग सूची-निर्माण कमांड के लिए महत्वपूर्ण हो सकता है।
पीटर कॉर्ड्स

1
@PeterCordes, यह वहां है इसलिए हमें IFS को पहले से निर्धारित करने पर कोई धारणा बनाने की आवश्यकता नहीं है।
स्टीफन चेजलस


0

जैसा कि दूसरों ने सुझाव दिया है, आप bc का उपयोग कर सकते हैं:

i="4.00"

while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
    # do something with i
    i="$(bc <<< "$i + 0.02")"
done
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.