如何使用Applescript在列表中找到最大值的索引?

时间:2018-07-14 02:39:40

标签: list indexing applescript

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

2 个答案:

答案 0 :(得分:3)

这有两个阶段:

  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来获得: / p>

    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循环算法。

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

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

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

答案 1 :(得分:1)

借助AppleScriptObjC和键值编码可以很容易地确定最大值

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

use framework "Foundation"

set numberList to {18, 18, 18, 18, 22, 21, 22}
set nsNumberList to current application's NSArray's arrayWithArray:numberList
set maxValue to nsNumberList's valueForKeyPath:"@max.self" -- 22

如果只有该值的一次出现,或者只希望第一次出现的索引写出

set maxIndex to ((nsNumberList's indexOfObject:maxValue) as integer) + 1 -- AppleScript's lists are 1-based

如果多次出现该值,并且您需要所有索引都使用循环(不幸的是,AppleScriptObjC中不提供有效的本机Cocoa API indexesOfObjectsPassingTest:

set maxIndexes to {}
repeat with i from 0 to (count numberList) - 1 -- Cocoa's lists are 0-based
    if (maxValue's isEqualToNumber:(nsNumberList's objectAtIndex:i)) then
        set end of maxIndexes to i + 1
    end if
end repeat
maxIndexes -- {5, 7}