Есть ли способ передать что-то более продвинутое, например метод или сравнение с функцией?

#javascript #function #applescript

Вопрос:

Поэтому я пытался переделать некоторые Array.prototype функции из Javascript в Applescript. Пытаясь это сделать, я заметил, что многие функции Javascript используют то, что, как мне кажется, называется функцией стрелки.

Вот небольшой обзор того, что я понимаю из этого:

Функции со стрелками строятся следующим образом (показано методом Array.prototype.filter() -):

 words.filter(word => word.length > 6);
 ^^^    ^^^  ^^^  ^^      ^^^
  1      2    3   4        5
 

Пример взят из mozilla.org.

  1. Это указывает целевой массив/список. Легко реализовать в Applescript.
  2. Это определяет используемую встроенную функцию. Я понятия не имею, как заставить это работать, потому что я не знаю, как реализовать шаги 3, 4 и 5.
  3. Это присваивает значение, которое «назначено» каждому элементу в списке. Это можно было бы сделать статически с помощью чего-то вроде repeat with word in words ... (за исключением того факта, что эти слова зарезервированы.), но я не знаю способа сделать это динамически.
  4. Это знак, который «указывает» компилятору, что должно произойти. Я не думаю, что это необходимо реализовывать.
  5. Это (в данном случае) сравнение того, имеет ли данное слово большее количество символов, равное 6. Это решает, должен ли предмет оставаться внутри или нет. Это можно было бы воссоздать с помощью if count of characters of word > 6 then set end of someNewListWeCreatedOutsideThisLoop to word .

Эти функции со стрелками также могут вместо сравнений иметь такие функции, как forEach() :

 array1.forEach(element => console.log(element));
 

Пример взят из mozilla.org.

Вот что я попробовал:

 on myFunc(fn)
  fn
end
myFunc(log "Hello World")
 

Это регистрирует «Привет, мир», а затем выдает ошибку о том, что было передано недостаточно параметров.

Вот немного хитрого обходного пути с использованием командной строки:

 set theWords to {"These", "are", "Words."}

forEach(theWords, "theWord => display dialog theWord")

on forEach(theArray, arrowFunction)
    set AppleScript's text item delimiters to " => "
    set arrowFunction to text items of arrowFunction
    set AppleScript's text item delimiters to " "
    return (do shell script "osascript -e 'repeat with " amp; item 1 of arrowFunction amp; " in (every word of "" amp; (theArray as string) amp; "")' -e '" amp; item 2 of arrowFunction amp; "' -e 'end repeat'")
end forEach
 

Этот метод работает, но у него утомительный синтаксис, скорость работы низкая, и я почти уверен, что упустил какой-то стильный метод Applescript.

Комментарии:

1. Сам по себе AppleScript является довольно минимальным языком и не имеет таких функций, как функции фильтрации или возможность передачи блоков. Вам нужно будет создать свой собственный обработчик(ы), используя функции, которые есть в языке AppleScript , чтобы выполнить эквивалент определенной функции.

2. Вместо того, чтобы пытаться реализовать функции из JavaScript в AppleScript, почему бы просто не использовать JavaScript? У него гораздо больше функций (ну, почти все работает), и в качестве бонуса его также можно использовать в редакторе сценариев.

3. @red_menace Мне нравится JavaScript, и я регулярно его использую, но я не попал в JXA, так как документация не так хороша, и я просто люблю Applescript.

4. @red_menace: JXA-мертвый продукт. Его поддержка событий Apple отключена, нет никакой документации или поддержки, которая стоила бы гроша, и он не интегрирован с остальной экосистемой JavaScript (узла). JXA так сильно провалилась, что Apple распустила команду автоматизации Mac и уволила ответственного за это премьер-министра. AppleScript-не очень хороший язык, но это единственный номинально поддерживаемый вариант, который работает правильно. (Для тех, кто любит жить опасно, python appscript и SwiftAutomation все еще работают, но я не предоставляю поддержку ни для того, ни для другого. NodeAutomation нарушена из-за bitrot в стороннем модуле NodObjC.)

5. Для тех, кто менее знаком с JS, word => word.length > 6 это просто синтаксический сахар для function (word) {return word.length > 6} , который является традиционным синтаксисом JS для анонимных (т. Е. безымянных) функций. Оно более лаконично, но смысл точно такой же.

Ответ №1:

Оберните обработчик, содержащий ваше пользовательское поведение, в объект сценария. Затем вы можете передать объект сценария в качестве параметра другому обработчику и вызвать его там.

 script Foo
    to doStuff(a, b)
        return a   b
    end doStuff
end script

to bar(obj)
   obj's doStuff(1, 2)
end bar

bar(Foo) 
--> 3
 

ETA: Фильтрация списка в JavaScript:

 [1, 4, 6, 2].filter(function (n) {return n < 3})
// [ 1, 2 ]
 

Фильтрация списка в AppleScript (с помощью библиотеки списков):

 use script "List"

script FilterObj
    to filterItem(n)
        return n < 3
    end filterItem
end script

filter list {1, 4, 6, 2} using FilterObj
--> {1, 2}
 

AppleScript более подробен, но они функционально эквивалентны.

Комментарии:

1. Как это поможет в фильтрации списка для определенных элементов или вызове метода для каждого элемента в списке?

2. Примеры можно найти здесь , например search text filter list , sort list команды.

3. Спасибо, что указали мне на это. Я и не знал, что такое существует.

4. Было бы интересно посмотреть, как вы будете реализовывать эту конкретную задачу фильтрации списков. Потому что я мало что знаю о тонкостях JavaScript.

Ответ №2:

Обновленный ответ. Я думаю, что теперь это точный эквивалентный пример закрытия JXA.

 on wordsFilter(theWords, function) -- this is your prototype
    
    if text 1 thru 19 of function is not "words whose length " then error
    set compareSign to text 20 thru 21 of function
    if compareSign is not in {"> ", "< "} then error
    set N to (text 22 thru -1 of function) as integer
    set filteredList to {}
    
    script One -- this creates closure like in the Javascript
        repeat with anItem in theWords
            if length of anItem > N then set end of filteredList to contents of anItem
        end repeat
        return filteredList
    end script
    
    script Two -- other closure 
        repeat with anItem in theWords
            if length of anItem < N then set end of filteredList to contents of anItem
        end repeat
        return filteredList
    end script
    
    if compareSign is "> " then return (run One)
    if compareSign is "< " then return (run Two)
    
end wordsFilter

set theWords to {"ThisIsGreaterThan6", "are", "otherBigWord"}
wordsFilter(theWords, "words whose length > 6")
 

Комментарии:

1. Поскольку prototype обработчику передается только результат от filterList обработчика, чем это отличается от простого вызова filterList обработчика в первую очередь?

2. Хотя это позволяет вызывать одну функцию внутри другой и позволяет фильтровать списки по длине слов, это не позволяет пользователю фильтровать более сложные вещи, такие как, например, проверка того, содержит ли слово подстроку или другие вещи. Я думаю, что тогда это невозможно, ха.

3. RE: @red_menace: Насколько я понимаю, разница чисто эстетическая. Под именем функции прототипа вы получаете группу обработчиков, выполняющих аналогичные задачи. Но, может быть, я ошибаюсь и есть другие преимущества.

4. OP спрашивает, как передавать функции, которые в JS являются замыканиями , в качестве аргументов другим функциям. Обработчики AppleScript не являются закрытиями (они не фиксируют свою лексическую область, поэтому их нельзя безопасно передавать); однако объекты сценария AppleScript запоминают свою область и могут быть созданы и переданы как объекты первого класса. Объектная система AppleScript здесь аналогична Smalltalk/Obj-C (1.0) в том, что вы создаете объекты и вызываете обработчики (методы) для этих объектов. Библиотеки AS, которые я связал выше, показывают, как это сделать правильно.

5. @foo, я обновил свой ответ в соответствии с вашей заметкой об областях применения.