ความผิดพลาดในฟังก์ชั่นเชลล์เพื่อนับจำนวนคู่


12

สำหรับการมอบหมายฉันต้องเขียนฟังก์ชั่นที่พิมพ์จำนวนคู่เมื่อจัดลำดับหมายเลข

ฉันใช้ชิ้นส่วนของรหัสที่ฉันใช้สำหรับการบ้านก่อนหน้า (เพื่อพิมพ์1เมื่อตัวเลขเป็นเลขคู่และ0เมื่อเลขคี่)

ปัญหาของฉันตอนนี้คือฟังก์ชั่นของฉันยังคงพิมพ์0อยู่ ผมทำอะไรผิดหรือเปล่า?

นี่คือสคริปต์ของฉัน:

#!/usr/bin/bash
# File: nevens.sh

# Write a function called nevens which prints the number of even numbers when provided with a sequence of numbers.
# Check: input nevens 42 6 7 9 33 = output 2

function nevens {

        local sum=0

        for element in $@
        do
                let evencheck=$(( $# % 2 ))
                if [[ $evencheck -eq 0 ]]
                then
                        let sum=$sum+1
                fi
        done

        echo $sum
}

2
เขียนใน shebang '#! / usr / bin / bash -x' จากนั้นคุณจะเห็นว่าเกิดอะไรขึ้น
Ziazis

+1 สำหรับบอกเราว่าเป็นการบ้าน - และสำหรับการทำงานหนักพอที่จะรับความช่วยเหลือได้
โจ

คำตอบ:


20

คุณลืมแทนที่$#ด้วย ( $) elementในforลูป:

function nevens {
  local sum=0
  for element in $@; do
    let evencheck=$(( element % 2 ))
    if [[ $evencheck -eq 0 ]]; then
      let sum=sum+1
    fi
  done
  echo $sum
}

ตอนนี้เพื่อทดสอบฟังก์ชั่น:

$ nevens 42 6 7 9 33
2
$ nevens 42 6 7 9 33 22
3
$ nevens {1..10..2} # 1 to 10 step 2 → odd numbers only
0
$ nevens {2..10..2} # 2 to 10 step 2 → five even numbers
5

17

@dessertพบปัญหาหลักฉันจะให้รีวิวรหัส:

  1. Shebang: ไม่มี/usr/bin/bashใน Ubuntu /bin/bashมัน
  2. เป็นเรื่องดีที่คุณได้ประกาศsum localและหลีกเลี่ยงการสร้างเนมสเปซตัวแปรนอกฟังก์ชั่น นอกจากนี้คุณสามารถประกาศตัวแปรจำนวนเต็มโดยใช้-iตัวเลือก:

    local -i sum=0
  3. พูดตัวแปรของคุณ (และพารามิเตอร์) เสมอ! มันไม่จำเป็นในสคริปต์นี้ แต่เป็นนิสัยที่ดีในการเข้าใช้:

    for element in "$@"
    do

    ที่กล่าวว่าคุณสามารถละเว้นที่in "$@"นี่:

    for element
    do

    เมื่อin <something>ไม่ได้รับforลูปจะวนซ้ำโดยนัย สิ่งนี้สามารถหลีกเลี่ยงข้อผิดพลาดเช่นการลืมคำพูด

  4. ไม่จำเป็นต้องคำนวณแล้วตรวจสอบผลลัพธ์ คุณสามารถทำการคำนวณโดยตรงในif:

    if (( (element % 2) == 0 ))
    then
        ((sum = sum + 1))
    fi

    (( ... ))เป็นบริบททางคณิตศาสตร์ มันมีประโยชน์มากกว่า[[ ... ]]การทำการตรวจสอบทางคณิตศาสตร์และนอกจากนี้คุณสามารถละเว้น$ตัวแปรก่อนหน้า (ซึ่งทำให้ง่ายต่อการอ่าน IMHO)

  5. หากคุณย้ายส่วนที่ตรวจสอบอย่างสม่ำเสมอไปยังฟังก์ชั่นที่แยกต่างหากมันอาจปรับปรุงความสามารถในการอ่านและการนำกลับมาใช้ใหม่:

    function evencheck
    {
        return $(( $1 % 2 ))
    }
    function nevens
    {
        local -i sum=0
        for element
        do
            # `if` implicitly checks that the returned value/exit status is 0
            if evencheck "$element"
            then
                (( sum++ ))
            fi
        done
        echo "$sum"
    }

1
หากคุณกำลังมองหาการใช้ตัวลูปsum=$((sum + 1 - element % 2))เทอร์เซอร์
David Foerster

1
@DavidFoerster Terser และอ่านน้อยกว่า ;-)
Konrad Rudolph

@DavidFoerster: ฉันมีความคิดแบบเดียวกันกับที่คุณควรใช้ผลลัพธ์ mod-2 โดยตรง (หรือดีกว่า&1เพื่อตรวจสอบบิตต่ำหากคุณสามารถอ่านได้มากขึ้น) แต่เราสามารถทำให้มันสามารถอ่านเพิ่มเติมได้ที่: ใช้ผกผันบูลแทนsum=$((sum + !(element&1) )) +1 - conditionหรือเพียงแค่นับองค์ประกอบแปลกด้วย((odd += element&1))และในตอนท้ายพิมพ์ด้วยเพราะecho $(($# - element)) even = total - odd
Peter Cordes

การทำงานที่ดีและดีที่จะชี้ให้เห็นสิ่งเล็ก ๆ ที่เริ่มต้นพลาดเช่นและlocal -i sum++
Paddy Landau

4

ฉันไม่แน่ใจว่าคุณเปิดรับโซลูชั่นอื่นหรือไม่ นอกจากนี้ฉันไม่ทราบว่าคุณสามารถใช้ยูทิลิตีภายนอกหรือถ้าคุณ จำกัด อยู่เพียง bash builtins grepตัวอย่างเช่นหากคุณสามารถใช้ฟังก์ชันของคุณอาจง่ายกว่านี้มาก:

function nevens {
    printf "%s\n" "$@" | grep -c '[02468]$'
}

สิ่งนี้ทำให้แต่ละจำนวนเต็มเข้าในบรรทัดของตัวเองแล้วใช้grepในการนับบรรทัดที่ลงท้ายด้วยเลขคู่


อัปเดต - @PeterCordes ชี้ให้เห็นว่าเราสามารถทำได้โดยไม่ต้องใช้ grep - เพียงแค่ทุบตีบริสุทธิ์ตราบใดที่รายการอินพุตมีจำนวนเต็มที่เกิดขึ้นเป็นอย่างดี (โดยไม่มีจุดทศนิยม):

function nevens{
    evens=( ${@/%*[13579]/} )
    echo "${#evens[@]}"
}

สิ่งนี้ทำงานได้โดยการสร้างรายการที่เรียกว่าevensโดยการกรองอัตราต่อรองทั้งหมดแล้วส่งคืนความยาวของรายการนั้น


แนวทางที่น่าสนใจ แน่นอนว่านี่จะนับ3.0เป็นเลขคู่ คำถามไม่แม่นยำเกี่ยวกับตัวเลขที่จะใช้
G-Man กล่าวว่า 'Reinstate Monica'

@ G-Man เนื่องจากทุบตีไม่สนับสนุนการคำนวณเลขทศนิยมมันปลอดภัยที่จะสมมติว่าเป็นจำนวนเต็ม
muru

เนื่องจากคำถามที่กล่าวถึงตัวเลข "คู่" (และโดยนัย "แปลก") ตัวเลขจึงค่อนข้างปลอดภัยที่จะสมมติว่ามันกำลังพูดถึงจำนวนเต็ม ฉันไม่เห็นว่าการสรุปข้อสงสัยเกี่ยวกับความสามารถและข้อ จำกัด ของเครื่องมือที่ผู้ใช้คาดว่าจะใช้ได้นั้นเป็นอย่างไร โปรดจำไว้ว่า: คำถามบอกว่านี่เป็นงานที่มอบหมาย - ฉันเดาว่าโรงเรียนเพราะนี่เป็นเรื่องที่ไม่สำคัญสำหรับงาน ครูในโรงเรียนเกิดเรื่องบ้าๆบอ ๆ
G-Man กล่าวว่า 'Reinstate Monica'

2
Bash สามารถกรองรายการได้ด้วยตัวเองถ้าเราสามารถสันนิษฐานได้ว่าไม่มีองค์ประกอบใดที่มีช่องว่าง: ขยายรายการ arg ด้วยตัวเลขคี่แทนที่ด้วยสตริงว่างโดยใช้${/%/}บน@อาเรย์เพื่อต้องการจับคู่ที่ส่วนท้ายของสตริง พิมพ์จำนวน foo(){ evens=( ${@/%*[13579]/} ); echo "${#evens[@]} even numbers"; printf "%s\n" "${evens[@]}"; }; foo 135 212 325 3 6 3 4 5 9 7 2 12310ในฐานะที่เป็นหนึ่งซับในการกำหนดและเรียกใช้: รวมถึงการพิมพ์รายการสำหรับการดีบัก ภาพพิมพ์5 even numbers 212 6 4 2 12310(บนสายของเดือนกันยายน)
Peter Cordes

1
อาจเป็นไปได้ว่า ZSH มีบางสิ่งบางอย่างในการกรองรายการอย่างถูกต้องแทนที่จะขึ้นอยู่กับการแบ่งคำเพื่อสร้างอาร์เรย์ใหม่ (ฉันรู้สึกประหลาดใจเล็กน้อยที่ทุบตีไม่ได้ใช้การขยายสเกลาร์สเกลาร์กับทุกองค์ประกอบเท่านั้น) สำหรับรายการที่มีขนาดใหญ่ผมจะไม่ต้องแปลกใจถ้าเป็นจริงได้เร็วกว่าgrep bashอืมฉันสงสัยว่ามีคำถาม codegolf ที่ฉันสามารถโพสต์ฟังก์ชั่นทุบตีที่: P
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.