排序传递给函数作为参数的阵列 [英] Sorting an array passed to a function as parameter

查看:123
本文介绍了排序传递给函数作为参数的阵列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想对数组排序了一些辅助功能,那我就可以
每当我需要它来使用。所以我做了这样的事情。它的工作原理。

 #!的/ usr /斌/庆典#排序给出的参数数组
功能array_sort {    声明-a source_array =($ {!1})
    sorted_array =($(在elmnt$ {source_array [@]};做回声$ elmnt;完成|排序))
    回声$ {sorted_array [@]}#不会呼应到标准输出,因为赋值给一个变量
}# 测试用例#原单阵列
ARR =(C B A 3 2 1)#assign函数调用给一个变量
分类= $(array_sort改编[@])#回声出结果
回声$ {分类[@]}

我的问题是,有没有这样做,在返回从函数数组元素方面的一些更好的(清洁剂)的方式(不执行更好的排序算法)?


解决方案

如果你想有一个强大的排序功能(即,一个将处理空格和换行完美),你可能会考虑实施猛砸排序算法:这里有一个快速排序。

 快速排序(){
    #排序位置元件WRT字母排序
    #返回的是数组quicksort_ret
    如果(($#== 0));然后
        quicksort_ret =()
        返回
    科幻
    局部轴= $ 1时=()=低()我
    转移
    对于i;做
        如果[$我< $支点]];然后
            低+ =($ I)
        其他
            更大+ =($ I)
        科幻
    DONE
    快速排序$ {更大[@]}
    更大=($ {quicksort_ret [@]})
    快速排序$ {较低[@]}
    quicksort_ret + =($支点$ {更大[@]})
}
$快速排序C B A 3 2 1
$ printf的'%s的\\ n'$ {quicksort_ret [@]}
1
2
3
一个
b
C

您可以更改顺序测试中的行

 如果[$我< $支点]];然后

通过任何你喜欢的。例如,数值只排序,你会使用

  IF((I<枢));然后

您甚至可以使用一个变量(例如,quicksort_order),将扩大到排序功能。在这种情况下,通过更换前行

 如果$ quicksort_order$ I$支点;然后

和搭配,例如使用的,如果你想字母排序:

  order_alnum(){[[$ 1< $ 2]]; }
quicksort_order = order_alnum

快速排序函数使用的输入和位置参数变量 quicksort_ret 输出。它现在微不足道,使包装解决这个函数来处理数组名作为输入。


有关,像你的,使用排序,但修复了通配符和空间的问题(但不换行解决问题)的方法。使用内置的映射文件,所以这只是Bash≥4。对于bash< 4,有其他解决方法(但你不应该使用bash 4;再反正)。

 #!的/ usr /斌/庆典#排序给出的参数数组
#返回的是数组sorted_array
array_sort(){
    映射文件-t sorted_array< ≤(printf的'%S \\ n'{!1} $|排序)
}#TEST CASE 1
回声测试1
#原数组
ARR =(C B A 3 2 1)
#数组排序
array_sort常用3 [@]
#显示阵列
声明-psorted_array#TEST CASE 2
回声测试2
#原数组
ARR =('*''这个领域的空间)
#数组排序
array_sort常用3 [@]
#显示阵列
声明-psorted_array#TEST CASE 3(失败)
回声测试3
#原数组
ARR =($'有\\钠换行符\\ n在这阵)
#数组排序
array_sort常用3 [@]
#显示阵列
声明-psorted_array

将输出:

 测试1
声明-a sorted_array ='([0] =1[1] =2[2] =3[3] =一个[4] =b的[5] =C)
测试2
声明-a sorted_array ='([0] =*[1] =在这一领域的空间)'
测试3
声明-a sorted_array =([0] =换行[1] =此数组中的[2] =有)'


回答您的问题评论:


  

这样一来我就必须知道 sorted_array 变量的名称,使用它在我的脚本。可以在避免?


如果你想给排序数组的名称,修改 array_sort 为:

  array_sort(){
    #$ 1数组名称进行排序(与尾随[@])
    #$ 2是返回数组的名称(不带[@])
    #注:输出数组的名字可以命名输入数组
    映射文件-t$ 2< ≤(printf的'%S \\ n'{!1} $|排序)
}

和使用:

  $ a =(A GëžĴR)
$ array_sort一[@]a_sorted
$申报-p a_sorted
声明-a a_sorted ='([0] =一个[1] =e的[2] =G[3] =J[4] =R[5] =Z)

如果你想使用快速排序函数从我的第一个答案,你会使用一个包装函数(这个名字不好意思)(*)

  quicksort_gniourf(){
    #$ 1数组名称进行排序(与尾随[@])
    #$ 2是返回数组的名称(不带[@])
    #注:输出数组的名字可以命名输入数组
    #这是围绕快速排序功能的包装函数
    快速排序$ {!1}
    当地的K = 0 V
    声明-g$ 2 =()
    在V$ {quicksort_ret [@]};做
        printf的-v$ 2 [$ K]'%s'的$ V
        ((+ K))
    DONE
}

和(在这里我使用的输入和输出相同的数组名)是:

  $ a =(A GëžĴR)
$ quicksort_gniourf一个[@]一
$声明-p一
声明-a一个='([0] =一个[1] =e的[2] =G[3] =J[4] =R[5] =Z)'


  

此外,你会如何回应出结果数组,preventing 的扩展* 声明-p 它是确定,但与的printf 或回声它扩展了文件名?


要打印一个数组阵列使用回声擅自扩大通配符(注意引号):

 回声$ {数组[@]}

和使用的printf ,每行一个字段(注意引号):

 的printf'%s的\\ n'$ {数组[@]}


(*)由于@konsolebox提到在他的评论,声明-g 出现在bash 4.2。您可以替换的eval$ 2 =()如果你喜欢(它在,因为这一点是相当安全的 $ 2 被认为是一个变量名反正)。

I wanted to have some helper function for array sorting, that I would be able to use whenever I need it. So I did something like this. It works.

#!/usr/bin/bash

# sorts an array given as param
function array_sort {

    declare -a source_array=("${!1}")
    sorted_array=($(for elmnt in "${source_array[@]}"; do echo $elmnt; done | sort))
    echo "${sorted_array[@]}" # wont echo to stdout, because of assignment to a variable
}

# TEST CASE

# orginal array
arr=(c b a 3 2 1)

#assign function call to a variable
sorted=$(array_sort arr[@])

# echo-out the results
echo ${sorted[@]}

My question is, is there some better(cleaner) way of doing this, in terms of returning array elements from function (not implementing better sorting algorithm)?

解决方案

If you want a robust sorting function (i.e., one that will deal with spaces and newlines flawlessly), you may consider implementing a sorting algorithm in Bash: here's a quicksort.

quicksort() {
    # sorts the positional elements wrt alphanumerical sort
    # return is in array quicksort_ret
    if (($#==0)); then
        quicksort_ret=()
        return
    fi
    local pivot=$1 greater=() lower=() i
    shift
    for i; do
        if [[ "$i" < "$pivot" ]]; then
            lower+=( "$i" )
        else
            greater+=( "$i" )
        fi
    done
    quicksort "${greater[@]}"
    greater=( "${quicksort_ret[@]}" )
    quicksort "${lower[@]}"
    quicksort_ret+=( "$pivot" "${greater[@]}" )
}


$ quicksort c b a 3 2 1
$ printf '%s\n' "${quicksort_ret[@]}"
1
2
3
a
b
c

You can change the ordering test in the line

if [[ "$i" < "$pivot" ]]; then

by whatever you like. E.g., for numerical only sort, you'd use

if ((i<pivot)); then

You can even use a variable (e.g., quicksort_order) that will expand to an ordering function. In this case, replace the former line by

if $quicksort_order "$i" "$pivot"; then

and use with, e.g., if you want alphanumerical sort:

order_alnum() { [[ $1 < $2 ]]; }
quicksort_order=order_alnum

The quicksort function uses the positional parameters for input and the variable quicksort_ret for output. It's now trivial to make a wrapper around this function to handle an array name as input.


For a method that, like yours, uses sort but fixes the issues with wildcards and spaces (but doesn't fix issues with newlines). Uses the builtin mapfile, so this is Bash≥4 only. For Bash<4, there are other workarounds (but you shouldn't be using Bash<4 anymore anyways).

#!/usr/bin/bash

# sorts an array given as param
# return is in array sorted_array
array_sort() {
    mapfile -t sorted_array < <( printf '%s\n' "${!1}" | sort )
}

# TEST CASE 1
echo "Test 1"
# original array
arr=(c b a 3 2 1)
# sort array
array_sort "arr[@]"
# display array
declare -p "sorted_array"

# TEST CASE 2
echo "Test 2"
# original array
arr=( '*' 'a space in this field' )
# sort array
array_sort "arr[@]"
# display array
declare -p "sorted_array"

# TEST CASE 3 (fails)
echo "Test 3"
# original array
arr=( $'there is\na newline\nin this array' )
# sort array
array_sort "arr[@]"
# display array
declare -p "sorted_array"

will output:

Test 1
declare -a sorted_array='([0]="1" [1]="2" [2]="3" [3]="a" [4]="b" [5]="c")'
Test 2
declare -a sorted_array='([0]="*" [1]="a space in this field")'
Test 3
declare -a sorted_array='([0]="a newline" [1]="in this array" [2]="there is")'


Answering your questions in comment:

So that way I would have to know the name of that sorted_array variable, to use it in my scripts. Can that be avoided?

If you want to give the name of the sorted array, modify array_sort as:

array_sort() {
    # $1 is the name of array to sort (with the trailing [@])
    # $2 is the name of the returned array (without [@])
    # Note: name of output array can be name of input array
    mapfile -t "$2" < <( printf '%s\n' "${!1}" | sort )
}

and use as:

$ a=( a g e z j r )
$ array_sort "a[@]" a_sorted
$ declare -p a_sorted
declare -a a_sorted='([0]="a" [1]="e" [2]="g" [3]="j" [4]="r" [5]="z")'

If you want to use the quicksort function from my first answer, you'd use a wrapper function (sorry about the name)(*):

quicksort_gniourf() {
    # $1 is the name of array to sort (with the trailing [@])
    # $2 is the name of the returned array (without [@])
    # Note: name of output array can be name of input array
    # This is a wrapper function around the quicksort function
    quicksort "${!1}"
    local k=0 v
    declare -g "$2=()"
    for v in "${quicksort_ret[@]}"; do
        printf -v "$2[$k]" '%s' "$v"
        ((++k))
    done
}

and use as (here I'm using the same array name for input and output):

$ a=( a g e z j r )
$ quicksort_gniourf "a[@]" a
$ declare -p a
declare -a a='([0]="a" [1]="e" [2]="g" [3]="j" [4]="r" [5]="z")'

Also, how would you echo out that resulting array, preventing expansion of *, with declare -p it is ok, however with printf or echo it expands on filenames?

To print an array array using echo without expanding wildcards (observe the quotes):

echo "${array[@]}"

and using printf, one field per line (observe the quotes):

printf '%s\n' "${array[@]}"


(*) As @konsolebox mentions in his comment, declare -g appeared in bash 4.2. You can replace this line with eval "$2=()" if you like (it's fairly safe at this point since $2 is supposed to be a variable name anyways).

这篇关于排序传递给函数作为参数的阵列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆