如何使用Applescript在列表中找到最大值的索引? [英] How do I find the index of the maximum value in the list using Applescript?

查看:141
本文介绍了如何使用Applescript在列表中找到最大值的索引?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个整数列表,例如{18,18,18,18,22,21},我想使用Applescript获取此列表的最大值,并获取最大的索引,请教我

解决方案

这有两个阶段:

  1. 确定列表中的最大值;
  2. 一旦知道最大值,就确定列表中该值最后一次出现的索引.

在下面演示的示例中,我将使用自己生成的示例列表.但是,您可以简单地将列表替换为我的列表,并且所描述的过程同样适用,并产生特定于您的输入的结果.

1.检索数值列表中的最大值

一种获取列表中最大值的快捷方法是使用bash数值sort命令,并选择最后一项:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}

    set text item delimiters to linefeed
    do shell script "sort -n <<<" & quoted form of (L as text) & "| tail -n 1"
    --> 78

但是,本着解决问题的精神,计算机科学家的方法是遍历列表中的项目并执行以下操作:

  • 存储第一项的值.
  • 如果下一项具有更大的价值,则将当前存储的价值替换为我们刚刚评估为具有更高价值的项目.
  • 如果下一项价值不大,请保留当前存储的值.

一旦到达列表的末尾,存储的值必须等于列表中的最大值项.目前,我们不知道它在列表中的位置,但是我们知道它的值.

这是执行此过程的AppleScript:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set max to L's first item

    repeat with x in L
        if x > max then set max to x's contents
    end repeat

    return max
    --> 78

2.确定列表中给定项目的索引

暂时不考虑最大值,问题的后半部分涉及能够确定任何给定项目在有序列表中的位置.

最明显的解决方案是,像以前一样,遍历列表中的每个项目并执行以下操作:

  • 如果当前项目等于目标项目,则将其索引附加到保留用于存储匹配索引的列表的末尾.

到达列表末尾时,匹配的索引列表将包含其值等于目标项目值的所有项目位置;否则匹配的索引列表将为空列表,表示主列表不包含我们寻求的值.

AppleScript列表中第一项的索引为1.使用列表的length属性获取整个列表中的项目数.

这是基本的AppleScript:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set matches to {}
    set target to 78

    repeat with i from 1 to L's length
        if item i of L = the target then set end of matches to i
    end repeat

    return the matches
    --> {3, 7, 10}

3.组合过程

将问题的两半组合起来就像按顺序运行每一半一样简单,请记住将过程的前半部分的结果(最大值)用作要在列表中寻找的目标值:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set max to L's first item
    # Get maximum value
    repeat with x in L
        if x > max then set max to x's contents
    end repeat

    set matches to {}
    set target to max
    # Get index of maximum value
    repeat with i from 1 to L's length
        if item i of L = the target then set end of matches to i
    end repeat

    return the matches
    --> {3, 7, 10}

最后,因为您只想要最大索引,所以它只是matches列表中的最后一个值,即10,您可以通过用以下行替换return the matches来获得该值:

    return the last item in matches
    --> 10

4.效率提高

已经概述了每个过程中的基本方法,但不一定是最快的方法.对于仅包含10个项目的列表,效率低下不是一个明显的问题.如果您有10,000个项目的列表,则希望能够减少获得结果的时间.

我认为我的说法是正确的,因为在算法改进方面没有明显的方法可以加快第一个过程:检索最大值需要比较每个商品的大小并保留最大商品./p>

但是,由于我们只需要确定列表中某项的 last 出现次数,就可以加快索引的确定时间.

因此,我们可以像以前一样运行该过程,但是要进行两个更改:

  1. 从列表的末尾开始,而不是开始.
  2. 找到第一个匹配项后,请停止该过程.

这是脚本:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set max to L's first item
    # Get maximum value
    repeat with x in L
        if x > max then set max to x's contents
    end repeat

    set target to max
    # Get index of maximum value
    repeat with i from L's length to 1 by -1
        if item i of L = the target then exit repeat
    end repeat

    return i
    --> 10

请注意,第二个repeat循环现在从最高索引向下运行到1;并且,我们不再需要matches列表,因此,我们只在找到匹配项时退出循环,然后查看我们在i处的值即可.

此处算法的另一项改进是测试了null情况,以确定我们是否真的需要遍历该列表:null情况是该列表不包含我们所需要的值的情况寻找. AppleScript提供了一种内置的方法来检查此问题:

    if the target is not in L then return 0

,此行将紧接在set target to max之后和repeat with i...之前.


5.高级改进

在解决了算法本身的效率之后,另一种提高效率的方法是解决脚本编写方式的效率.这比您现在需要关心的要先进,但是如果我编写供自己使用的脚本,这就是我可能如何实现算法的方法:

我将定义一个名为maximum()的处理程序,该处理程序将列表作为其参数并返回最大值,然后像下面这样实现该处理程序的脚本:

    on maximum(L as list)
        local L

        if L is {} then return {}
        if L's length = 1 then return L's first item

        script Array
            property x0 : L's first item
            property xN : rest of L
            property fn : maximum(xN)
            property predicate : x0 > fn
        end script

        tell the Array
            if its predicate is true then return its x0
            its fn
        end tell
    end maximum

这使用称为脚本对象的东西来处理列表中的项目,在AppleScript中,它比常规的迭代repeat循环要快得多.

接下来,我将定义一个名为lastIndexOf()的第二个处理程序,该处理程序将提供的值和一个列表作为其两个参数,并返回给定列表中提供的值出现的最高索引.我的处理程序如下所示:

    on lastIndexOf(x, L as list)
        local x, L

        if x is not in L then return 0
        if L = {} then return

        script Array
            property x0 : L's last item
            property xN : reverse of rest of reverse of L
            property predicate : x0 ≠ x
        end script

        # For the last match only:
        if Array's predicate is false then return (Array's xN's length) + 1

        # For a match list (comment out line above):
        tell the Array
            if its predicate is false then ¬
                return the lastIndexOf(x, its xN) ¬
                    & (its xN's length) + 1
            return lastIndexOf(x, its xN)
        end tell
    end lastIndexOf

然后,我要做的就是获得结果:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 14, 19, 78}
    get the lastIndexOf(maximum(L), L)
    --> 10

但是,不要尝试去理解我在这里所做的事情,而要专注于理解repeat循环算法.

我提供了这些更高级的版本以保持完整性,并且对于那些可能想知道为什么我没有提供我可能拥有的最佳解决方案的读者可能会感到好奇.

虽然这些高级版本中使用的算法保持不变(看起来不像,但实际上是这样),但是代码的编写方式使这些方法对于大型项目非常有效.

但是请注意,我没有包括任何错误处理,因此,如果您要向这些处理程序传递包含非数字项的列表,则至少要有一个他们当中的人会抱怨.

I have an int list such as {18, 18, 18, 18, 22, 21}, I want to use Applescript to get the maximum value of this list, and get the maximum index, please teach me

解决方案

There are two stages to this:

  1. Identifying the maximum value in the list;
  2. Once the maximum value is known, determining the index of the last occurrence of this value in the list.

I'll use a sample list I generated myself in the examples I demonstrate below. However, you can simply substitute your list for mine, and the processes described will work just as well, and produce the results specific to your input.

1. Retrieving the maximum value in a numerical list

A quick-and-dirty way to get the maxium value in the list is to use a bash numeric sort command, and pick the last item:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}

    set text item delimiters to linefeed
    do shell script "sort -n <<<" & quoted form of (L as text) & "| tail -n 1"
    --> 78

But, in the spirit of problem solving, the computer scientist's approach would be to iterate through the items in the list and perform these operations:

  • Store the first item's value.
  • If the next item is of greater value, then replace the currently stored value with the item we just assessed as being greater in value.
  • If the next item is not of greater value, retain the currently stored value.

Once you reach the end of the list, the stored value must be equal to the greatest value item in the list. At this point, we don't know its position in the list, but we know its value.

Here's the AppleScript that performs this process:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set max to L's first item

    repeat with x in L
        if x > max then set max to x's contents
    end repeat

    return max
    --> 78

2. Determining the index of a given item in a list

Putting aside the maximum value for now, the second half of the problem involves being able to determine the position of any given item in an ordered list.

The most obvious solution to this is, as before, iterating through each item in the list and performing this operation:

  • If the current item is equal to the target item, then append its index to the end of a list reserved for storing matching indices.

Once you reach the end of the list, your matched indices list will contain all the positions of the items whose value equal your target item's value; or the matched indices list will be an empty list, indicating that the main list does not contain the value we sought out.

The index of the first item in an AppleScript list is 1. Use the length property of a list to obtain the number of items in the whole list.

Here's a basic AppleScript:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set matches to {}
    set target to 78

    repeat with i from 1 to L's length
        if item i of L = the target then set end of matches to i
    end repeat

    return the matches
    --> {3, 7, 10}

3. The combined process

Combining these two halves of the problem is as simple as running each half in sequence, being mindful to use the result from the first half of the process—the maximum value—as the target value to be sought out in the list:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set max to L's first item
    # Get maximum value
    repeat with x in L
        if x > max then set max to x's contents
    end repeat

    set matches to {}
    set target to max
    # Get index of maximum value
    repeat with i from 1 to L's length
        if item i of L = the target then set end of matches to i
    end repeat

    return the matches
    --> {3, 7, 10}

Finally, as you only want the maximum index, this is simply the last value in the matches list, i.e. 10, which you obtain by replacing return the matches with this line:

    return the last item in matches
    --> 10

4. Efficiency improvements

Having outlined the basic methods in each process, these aren't necessarily the fastest methods. With lists containing only 10 items, inefficiency is not a noticeable concern. If you had a list of 10,000 items, you would want to be able to reduce the time to get your result.

I think I'm correct in stating that there's no discernible way to speed up the first process in terms of algorithmic improvements: retrieving the maximum value necessitates comparing every item's magnitude and retaining the largest.

Determining the index, however, can be sped up given that we only need to determine the last occurrence of an item in the list.

Therefore, we can run the process as before, but making two changes:

  1. Start from the end of the list instead the beginning.
  2. Stop the process once we find the first match.

Here's the script:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 4, 19, 78}
    set max to L's first item
    # Get maximum value
    repeat with x in L
        if x > max then set max to x's contents
    end repeat

    set target to max
    # Get index of maximum value
    repeat with i from L's length to 1 by -1
        if item i of L = the target then exit repeat
    end repeat

    return i
    --> 10

Note here the second repeat loop now runs backwards from the highest index down to 1; and, we no longer require the matches list, so instead we simply exit the loop when a match is found, and see what value of i we were at.

One further improvement to the algorithm here would be to test for the null case to determine whether or not we really need to run through the list at all: the null case is the case where the list doesn't contain the value we seek. AppleScript provides a builtin way to check this:

    if the target is not in L then return 0

and this line would sit immediately after set target to max and immediately before repeat with i....


5. Advanced improvements

Another way to improve efficiency, after addressing efficiency of the algorithms themselves, is to address the efficiency of the way the script is written. This is more advanced than you need to concern yourself with now, but here's how I'd probably implement the algorithms if I were writing the script for my own use:

I would define a handler called maximum() that takes a list as its argument and returns the greatest value, and I would implement the scripting of this handler like so:

    on maximum(L as list)
        local L

        if L is {} then return {}
        if L's length = 1 then return L's first item

        script Array
            property x0 : L's first item
            property xN : rest of L
            property fn : maximum(xN)
            property predicate : x0 > fn
        end script

        tell the Array
            if its predicate is true then return its x0
            its fn
        end tell
    end maximum

This uses something called a script object to process the items of the list, which is much, much quicker in AppleScript than conventional iterative repeat loop.

Next, I would define a second handler called lastIndexOf() that takes a supplied value and a list as its two arguments, and returns the highest index at which the supplied value occurs in the given list. My handler would look like this:

    on lastIndexOf(x, L as list)
        local x, L

        if x is not in L then return 0
        if L = {} then return

        script Array
            property x0 : L's last item
            property xN : reverse of rest of reverse of L
            property predicate : x0 ≠ x
        end script

        # For the last match only:
        if Array's predicate is false then return (Array's xN's length) + 1

        # For a match list (comment out line above):
        tell the Array
            if its predicate is false then ¬
                return the lastIndexOf(x, its xN) ¬
                    & (its xN's length) + 1
            return lastIndexOf(x, its xN)
        end tell
    end lastIndexOf

Then, all I need to do to obtain the result is:

    set L to {4, 24, 78, 32, 1.5, 32, 78, 14, 19, 78}
    get the lastIndexOf(maximum(L), L)
    --> 10

But, don't try and understand what I've done here just yet, and concentrate on understanding the repeat loop algorithms.

I've included these more advanced versions for completeness and for readers who may have wondered, had I left this out, why I didn't provide the most optimal solution I could have.

Whilst the algorithm used in these advanced versions remains the same (it doesn't look like it, but it is), the way the code is written makes these incredibly efficient for large itemed lists.

Note, however, I haven't included any error handling, so if you were to pass those handlers a list that contained non-numerical items, at least one of them would complain.

这篇关于如何使用Applescript在列表中找到最大值的索引?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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