How can Applescript objects be referenced before t

2019-07-25 05:58发布

问题:

Does anyone know why this applescript works? I do not understand why. The script generates three dialog boxes containing the same message: "Hi There". I have two questions:

1) How can j and k be set to reference i before i is defined?

2) Why does not r reference the i defined in test2?

on test1()
  return get a reference to i
end test

on run
  set j to test1()
  set k to a reference to i
  set i to "Hi there"
  display dialog j
  display dialog k
  test2()
end run

on test2()
  set i to "now see here"
  set r to a reference to i
  display dialog r
end test2

Note: The Script Editor is Version 2.7 and the AppleScript Version is 2.4.

回答1:

  1. You can only create references to an object property or element[s], or to global variables (an annoying mis-feature which behave like badly designed properties), not to local variables. e.g. These all work:

    on test2()
        set x to {i:"now see here"}
        set r to a reference to i of x
        display dialog r
    end test2
    
    test2()
    
    on test3()
        set x to {"now see here"}
        set r to a reference to item 1 of x
        display dialog r
    end test3
    
    test3()
    
    on test4()
        script x
            property i : "now see here"
        end script
        set r to a reference to i of x
        display dialog r
    end test4
    
    test4()
    
    
    property i : "now see here"
    
    on test5()
        set r to a reference to i
        display dialog r
    end test4
    
    test5()
    
  2. All variables within an implicit or explicit run handler are global (another mis-feature), unless explicitly declared local. The combination of those two mis-features is why your run handler example works, even though it looks like it shouldn't.

Yeah, it's kind of a janky language. But look on the bright side: it's still less headachey than C pointers.



回答2:

You basically don't want to create "reference to" in your scripting. The class exists because it is common for a command to return a reference to. The only advantage I can think of using it is that you CAN create a reference to an object, without the class and value of the object being explicitly given. You can then later define the object.

  1. So, in your sample, you CAN create a reference to an object and then later create it.

  2. Inside the test2 handler, a reference to is a global context, so it looks at the identifier i in the run handler, not inside the test2 handler.

I really suggest not using them in your script. What is the context you feel like you need to?



回答3:

I feel foo answered my second question on Nov 18. As for the first question, I will offer my own explanation, which I discerned after reading about Object Specifiers. The following statement, taken from my example, contains an implicit of (get me).

return get a reference to i

To illustrate, I will rewrite the statement explicitly.

return get a reference to i of (get me)

When either of these statements execute, a reference object is created which contains an object specifier. The phrase between the a reference to and the get is not evaluated, instead it is stored in the object specifier. In this case, the phrase is i of. What is to the right of the get is evaluated and this result is also stored in the object specifier. In this case, this result is me which is the top-level script object. This is why the variable i does not have to exist, when the reference object is returned from the test1 handler. When the display dialog j statement executes, the reference object is fully evaluated. It is at this point the variable i must exist. This same explanation can be applied to the variable k shown in my example.

I do realize that a text phrase, to be evaluated later, probably is not actually stored in an object specifier. At the very least, I know the phrase is parsed and syntax checked, but the AppleScript Language Guide does not definitely state how the unevaluated part of a statement is stored.

To me, the placement of the implicit get seems to be somewhat arbitrary. Let me illustrate my point with some examples. (Here for brevity, I have omitted proofs. If reader(s) can not verify, let me know and I will include proofs)

Example 1: If you write

set r to a reference to i

you get

set r to get a reference to i of (get me)

Example 2: If you write

set r to a reference to item 2 of i

you get

set r to get a reference to item 2 of (get i of me)

Example 3: If you write

set r to a reference to i of me

you get

set r to get a reference to i of (get me) -- same as Example 1

Example 4: If you write

set r to a reference to item 2 of i of me

you get

set r to get a reference to item 2 of i of (get me) -- differs from Example 2

If you wish to set the contents of a reference to a object, I found the following restriction. The number of in and of reserved keywords in a phrase, contained by a object specifier, must equal unity. A total of zero or more than one will result in a script execution error. The example below illustrates this.

property j : {1, 2, {3, 4}, {{5, {6, 7}}, 8, 9}}

log "Line 1: " & j
set r to a reference to item 3 of j -- phrase is "item 3 of", object is (get j of me)
set contents of r to "aa" -- Succeeds since total count is 1
log "Line 2: " & j

set r to a reference to item 2 of (get item 1 of item 4 of j) -- phrase is "item 2 of", object is (get item 1 of item 4 of (get j of me)) 
set contents of r to "bb" -- Succeeds since total count is 1
log "Line 3: " & j

set j to {1, 2, {3, 4}, {{5, {6, 7}}, 8, 9}}
log "Line 4: " & j
set r to a reference to item 3 of j of me -- phrase is "item 3 of j of", object is (get me)
try
    set contents of r to "cc"  -- Fails since total count is 2 
on error msg
    log "Line 5: " & msg
end try
log "Line 6: " & j

set r to a reference to item 2 of item 1 of item 4 of j -- phrase is "item 2 of item 1 of item 4 of", object is (get j of me) 
try
    set contents of r to "dd" -- Fails since total count  is 3
on error msg
    log "Line 7: " & msg
end try
log "Line 8: " & j

The log output is given below.

(*Line 1: 123456789*)
(*Line 2: 12aa56789*)
(*Line 3: 12aa5bb89*)
(*Line 4: 123456789*)
(*Line 5: Can’t set item 3 of j to "cc".*)
(*Line 6: 123456789*)
(*Line 7: Can’t set item 2 of item 1 of item 4 of {1, 2, {3, 4}, {{5, {6, 7}}, 8, 9}} to "dd".*)
(*Line 8: 123456789*)

The statement set contents of r to "bb” worked because I added an explicit get to the previous statement. This forced early evaluation of part of the object specifier.

Next, I would like to address the question “What is the context you feel like you need to [use references]?” asked by tweaks on Nov 17.

Originally, I was trying to find a way for a handler to return two values through passed parameters. A working example is shown below.

set r to missing value
set s to missing value
FirstAndLast(a reference to r, a reference to s, "now is the time for all good men")
log r
log s

on FirstAndLast(alpha as reference, omega as reference, message as text)
    set contents of alpha to first word of message
    set contents of omega to last word of message
end FirstAndLast

Executing this code produces the log output:

(*now*)
(*men*)

The code, shown above, has two drawbacks: 1) The variables r and s can not be local. 2) The variables r and s have to exist before the handler FirstAndLast is called. After working with Applescript for a while, I realize there are better ways to implement this code. One way is to employ pattern assignment as described in the AppleScript Language Guide. The code using pattern assignment is shown below.

set {r, s} to FirstAndLast("now is the time for all good men")
log r
log s

on FirstAndLast(message as text)
    set alpha to first word of message
    set omega to last word of message
    return {alpha, omega}
end FirstAndLast 

This version has three advantages. 1) Here no reference objects are used. 2) The identifiers r and s can represent local variables, global variables or properties. 3) If the identifiers r and s represent variables, the variables do not have to exist before calling the handler FirstAndLast.

Finally, evidently handlers are objects. If you think I am wrong, then explain why the following executes.

on test()
    log class of test as text
end test
property y : test
set x to test
log class of x as text
log class of y as text
x()
y()


回答4:

This is not an answer to the original question. This is in response to foo’s comment made on December 1. It is an example of how to create two instances of the same script boy which share a common record. Also, having learned from foo's comment, I used a record to share properties with all the script instances. I don’t know if this is the best way to do it, but at least there are no globals.

script mom
    property hair : "blond"
    property shared : {address:"12 walnut st", phone:"555-1234"}
end script

script boy
    property parent : mom
end script

script girl
    property parent : mom
end script

on clone from old given shared:sharedSwitch as boolean : false
    if sharedSwitch then
        set shared to shared of old
        set shared of old to missing value
    end if
    copy old to new
    if sharedSwitch then
        set shared of new to shared
        set shared of old to shared
    end if
    return new
end clone

property myboy : clone from boy with shared

on run
    log "mom hair is" & tab & tab & hair of mom
    log "boy hair is " & tab & tab & hair of boy
    log "girl hair is " & tab & tab & hair of girl
    log "myboy hair is " & tab & hair of myboy
    set hair of boy to "redhead"
    log "**Set boy hair to redhead**"
    log "mom hair is" & tab & tab & hair of mom
    log "boy hair is " & tab & tab & hair of boy
    log "girl hair is " & tab & tab & hair of girl
    log "myboy hair is " & tab & hair of myboy
    set hair of myboy to "flattop"
    log "**Set myboy hair to flattop**"
    log "mom hair is" & tab & tab & hair of mom
    log "boy hair is " & tab & tab & hair of boy
    log "girl hair is " & tab & tab & hair of girl
    log "myboy hair is " & tab & hair of myboy
    log
    log "mom phone is" & tab & phone of shared of mom
    log "boy phone is" & tab & phone of shared of boy
    log "girl phone is" & tab & phone of shared of girl
    log "myboy phone is" & tab & phone of shared of myboy
    set phone of shared of myboy to "555-9999"
    log "**Set myboy phone to 555-9999**"
    log "mom phone is" & tab & phone of shared of mom
    log "boy phone is" & tab & phone of shared of boy
    log "girl phone is" & tab & phone of shared of girl
    log "myboy phone is" & tab & phone of shared of myboy
end run