間隔内の変数の条件の場合

間隔内の変数の条件の場合

いくつかのデータが時間間隔にわたって分散されており、時間間隔内でいくつかのデータを取得したいと思います。たとえば、1..9、11..19などの範囲にあるデータがありますが、1-2、11-12などの範囲にあるデータをインポートしたいと思います。

これは、この条件とデータをキャプチャできるタイミングを分離するループをbash含めるより複雑なスクリプトの一部になります。if

私は次のことを考えています:

if (( $t_initial & $t_final )) in (begin1, fin1) or in (begin2, fin2) ...

t_initialここで、合計はt_finalスクリプト自体によって別々に計算されます。

この条件を構文で作成することはできませんbash。他の解決策を見つけましたが、長すぎて不快に見えたので、ここでより簡単で読みやすい解決策を求めます。


コードが浮動小数点数で正しく機能することは非常に重要です。 OPソリューションを修正しようとしていますが、まだ方法が見つかりません。

答え1

この解決策が気に入るかもしれません。これには2つの特性があります。

  • 外部プログラムは不要
  • 関数を使用するので、少なくとも比較の複雑さを隠します。

これです:

#!/bin/bash

# The comparing function
function compareInterval {
 t1=$1
 t2=$2

 shift 2

 while (( "$2" )); do
   if ((  $t1 >= $1  &&  $t2 <= $2 )); then
     # got match
     return 0
   fi
   shift 2
 done

 return 1
}

# sample values
t_initial=2
t_final=4

# Invocation. Compares against 1-3, 3-5, 2-5
if compareInterval  $t_initial $t_final  1 3  3 5  2 5; then
 echo Got match
fi

答え2

bash範囲比較はデフォルトではサポートされておらず、浮動小数点数もサポートされていないため、直接実行する必要があります。また、関数を定義し、それをbc浮動小数点計算に使用します。最終結果とテストコレクションは次のとおりです。

# Call as `compareRanges start end b1 f1 b2 f2 b3 f3...`
compareRanges() {
    local t_initial=$1
    local t_final=$2
    shift 2
    while [ ${#@} -gt 1 ]
    do
        local in_range=$(bc <<<"$t_initial >= $1 && $t_final <= $2")
        if [ $in_range = 1 ]
        then
            # Debugging output to stderr - can be removed:
            echo "[$t_initial,$t_final] is within [$1,$2]" >&2
            return 0
        fi
        shift 2
    done
    # Debugging output to stderr - can be removed:
    echo "[$t_initial,$t_final] is not within any ranges." >&2
    return 1
}
# Basic integers from the example
compareRanges 1 3 2 4 && echo BAD || echo OK
compareRanges 1 3 1 3 && echo OK || echo BAD
compareRanges 1 3 0 4 && echo OK || echo BAD
# Fractional numbers
compareRanges 1.5 2.5 1.1 2.2 && echo BAD || echo OK
compareRanges 1.5 2.5 0.3 3.1 && echo OK || echo BAD
# Multiple ranges
compareRanges 5 7 1 4 2 6 3 9 && echo OK || echo BAD
compareRanges 5 7 1 2 3 4 5 6 7 8 && echo BAD || echo OK

このcompareRanges関数には2つ以上のパラメータが必要です。最初のものはあなたのものであり、t_initial2番目のものはあなたのものですt_final。その後、、、、、begin1順にfin1ペアbegin2で他のパラメータを任意の数だけ使用できますfin2

最初のテストケースは、質問コメントの範囲(1-3と2-4)を比較します。

compareRanges 1 3 2 4 && echo BAD || echo OK

だから、1だから、だから。t_initial3t_final2begin14fin1

複数の範囲を使用したい場合は、ペアで一覧表示できます。

compareRanges 5 7 1 4 2 6 3 9 && echo OK || echo BAD

ここでは、1-4、2-6、3-9についてテストします。whileループでは、各ペアを順番に見て、t_initialそれを合計と比較しますt_final

bash使用した素数はサポートされていないためbc、任意の精密電卓です。入力は次のように提供されます<<<"$t_initial >= $1" ...。標準入力に文字列を供給します。$1は、現在ループ反復で見ている範囲の始まりであり、$2下限と上限の両方を比較する終わりです&&。比較が真のときに出力され、bc比較が偽の場合に出力されます。結果を保存し、両方のテストが真の場合、関数は成功します()。10in_rangereturn 0

素数は通常の小数形式で指定できます。

compareRanges 1.5 2.5 0.3 3.1 && echo OK || echo BAD

bc小数点以下の桁数と必要なサイズの数字を処理します。

最後に、一致する境界ペアがないと失敗します(return 1)。この機能は次のように使用できます。

if compareRanges $t_initial $t_final 2 4 11 19
then
    ...
fi

テストスイートを実行すると、すべての「OK」を印刷する必要があります。


または他のシェル(例:zsh)する10進変数の値をサポートします。bc関数で比較を実行する方が良いですが、これらのいずれかでスクリプトを実行できる場合は、これらの問題を回避できます。少なくともzsh'の場合は浮動小数点数なので、必ずしも正確ではありませんbc

答え3

以下は、LatinSuDの浮動小数点処理に対するいくつかの答えの恥ずかしい盗作です。彼の答えは「外部プログラムは必要ありません」と自慢することがわかります。このプログラムはbc彼が提案したように電卓プログラムを使用します。

#!/bin/bash

# The comparing function
function compareInterval {
 t1=$1
 t2=$2

 shift 2

 while (( "$2" ))
 do
   # if ((  $t1 >= $1  &&  $t2 <= $2 ))
   bc_result=$(echo "print $t1 >= $1  &&  $t2 <= $2" | bc)
   if [  "$bc_result" = 1 ]
   then
     # got match
     return 0
   fi
   shift 2
 done

 return 1
}

# sample values
t_initial=2.3
t_final=4.2

# Invocation. Compares against 1-3, 3-5, 2-5
if compareInterval  $t_initial $t_final  1 3  3 5  2 5
then
 echo Got match
fi

これはif (( $t1 >= $1 && $t2 <= $2 ))テストされてから送信され、bc出力をキャプチャしますbc


もう 1 つの方法は、数値に 10 の乗算を乗算して数値を整数に正規化することです。これには最大小数点以下の桁数が必要です。たとえば、小数点の右側に3桁以上のデータポイントがない場合は、すべてのデータに1000を掛けることができます。

#!/bin/bash

# Normalize function: it multiplies a floating point number by 1000
# without using floating point arithmetic.
normalize()
{
  case "$1" in
    *.*)
        result=$(echo "$1"000 | sed 's/\(.*\)\.\(...\).*/\1\2/')
        ;;
    *)
        result="$1"000
  esac
  echo "$result"
}

# The comparing function
function compareInterval {
 t1=$(normalize $1)
 t2=$(normalize $2)

 shift 2

 while (( "$2" ))
 do
   a1=$(normalize $1)
   a2=$(normalize $2)
   if ((  $t1 >= $a1  &&  $t2 <= $a2 ))
   then
     # got match
     return 0
   fi
   shift 2
 done

 return 1
}

# sample values
t_initial=2.3
t_final=4.2

# Invocation. Compares against 1-3, 3-5, 2-5
if compareInterval  $t_initial $t_final  1 3  3 5  2 5
then
 echo Got match
fi

関数の引数がnormalize単純整数(例えば、小数点のない数字など17)の場合は、単に追加して1000を掛けることができる000ので1717000。引数がnormalize浮動小数点数の場合(たとえば、小数点が含まれている場合など42.5)、それでも追加してから、小数点と0003sed番目の数字の後のすべての項目を削除します。このsedコマンドはs/\(.*\)\.\(...\).*/\1\2/次の文字列を使用します。abcdefジケル そして戻ってくるabcdefghiだから42.5→→ (つまり42.50004250042.5×1000)。

この文字列操作はをbash使用せずに完全に実行できますsed

答え4

あなたが要求する「11..19」は支柱の拡張と呼ばれます。

あなたが使用できるeval {$t_initial..$t_final}

...または

if `seq $t_initial..$t_final`==$somevalue

関連情報