While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.Semiconductor Toolkit을 업데이트하는 동안 Recall 기능을 다시 빌드하는 더 나은 방법을 찾았습니다.
다음에서 배포 플랫폼을 엽니다. JMP . 오른쪽에 있는 작은 버튼 그룹, 특히 "Recall" 버튼을 살펴보세요. 그것은 어디에나 있습니다. JMP의 거의 모든 플랫폼에 이 기능이 있습니다. 이는 JMP 디자인 언어의 DNA에 담겨 있습니다. 이제 데이터와의 일상적인 전투에서 그 작은 버튼이 얼마나 자주 당신의 엉덩이를 구했는지 잠시 생각해보십시오. 많이 그렇죠? 이는 아마도 가장 당연하게 여겨지는 JMP 기능 중 하나일 것입니다. 하지만 저는 이것이 JMP를 지금처럼 유용하게 만드는 무수히 많은 작은 손길 중 하나라고 말하고 싶습니다.
이제 JSL 스크립터라면 사용자 정의 추가 기능에서 해당 Recall 기능을 복제하는 방법에 대해 잠시 생각해 보십시오. 이는 매우 고급 작업입니다. 대부분의 사람들(나 자신 포함)이 수행하는 방식은 호출하려는 각 GUI 요소에 대해 사용자 정의 또는 전역 네임스페이스에 변수를 생성하는 것과 관련됩니다. 런타임에 각 GUI 요소로부터 정보를 수집하는 지원 기능과 Recall 버튼을 눌렀을 때 정보를 다시 기록하는 지원 기능이 있습니다.그것. 이다. ㅏ. 왕실의. 통증.JMP 스크립트와 사용자 정의 응용 프로그램에서 회수 기능을 설정하는 것은 아마도 가장 힘든 작업 중 하나일 것입니다. GUI에 있는 하나 또는 두 개의 구성 요소의 경우 큰 문제는 아니지만 표준 프로세스는 확장이 잘 되지 않습니다. 그러나 이러한 노력에 대한 대가로 리콜 기능은 추가 기능을 새로운 수준의 전문성으로 끌어올리는 수준의 세련미와 사용 편의성을 제공합니다. 사용자는 추가 기능에 불러오기 기능이 있으면 좋아합니다. 그리고 솔직히 말해서 저는 코딩하는 것을 싫어합니다.
우리는 그것을 재건할 수 있어요
조금 백업해 보겠습니다. 이 게시물에서 논의하는 아이디어는 2016년 초에 시작되었습니다. 방금 업데이트를 시작했는데 반도체 툴킷 (STK)를 버전 2로 변경합니다. 다른 블로그 게시물에서 다루게 될 여러 가지 이유로 저는 전체 프로젝트를 다음으로 마이그레이션하기로 결정했습니다. 애플리케이션 빌더 ~에 JMP .이는 코드 베이스와 GUI를 수정하고, 정리하고, 가능한 경우 코드를 표준화하고, 동료가 기여한 몇 가지 새로운 기능을 추가하는 것을 의미했습니다(다시 한 번 감사드립니다!). 불행하게도 이 작업으로 인해 버전 1의 Recall에 대한 기존 코드가 모두 손상되었습니다. 추가 기능의 모든 플랫폼에 대한 Recall 코드를 다시 작성하는 작업에 직면하면서 저는 “이 문제에 접근하는 다른 방법이 있어야 합니다. 반복적인 코딩을 모두 줄일 수 있는 방법이 있어야 합니다.”
비슷한 시기에 STK의 다른 기능을 위해 JSL의 연관 배열(AA)을 사용하는 방법을 연구하고 있었습니다. 사람들이 AA를 많이 사용하지는 않지만 AA는 스크립팅 무기고에서 믿을 수 없을 만큼 강력한 도구가 될 수 있습니다. 이 논의를 위해 AA는 특별한 종류의 목록입니다. 검색하고, 물건을 넣고, 꺼내는 등의 작업을 할 수 있습니다. 이를 특별하게 만드는 것은 목록의 각 항목이 키와 값이라는 두 부분으로 구성되어 있다는 것입니다. AA에 대한 훌륭한 블로그 게시물과 리소스가 있습니다( JMP 문서 연관 배열 목록을 데이터 테이블로 변환하는 방법 JSL 팁: 목록에서 고유한 값 찾기 ) 제가 AA 작업을 할 때 참고하는 내용입니다. 그들은 AA에 대한 많은 자료를 다루며 몇 가지 훌륭한 예를 가지고 있습니다. 원래의 문제점으로 돌아가서 전역 변수 이름과 해당 내용이 AA의 키-값 쌍으로 변환될 수 있다는 생각이 들었습니다. 그래서 순간의 뮤즈로서 더 나은 Recall을 구축하기 위해 연관 배열을 사용할 수 있는지 궁금했습니다.
좋습니다. 제가 생각한 내용을 이해하는 데 도움이 되도록 이것이 일반적으로 어떻게 수행되는지 살펴보겠습니다. Recall을 구현하려면 두 가지 기본 작업이 필요합니다. 사용자가 "확인"을 클릭하는 것과 같은 일부 트리거 작업을 수행하면 호출할 모든 GUI 요소(예: 목록 상자, 라디오 상자, 텍스트 상자 등)의 내용을 어떤 방식으로든 가져와 저장해야 합니다. 그런 다음 사용자가 "Recall" 버튼을 클릭하면 저장된 콘텐츠가 GUI의 적절한 위치로 다시 푸시되어야 합니다. 이 두 작업의 중심에는 JSL 메시지 쌍이 있습니다. << get() 그리고 << 세트() . GUI 요소에 따라 다양한 맛이 있습니다(이유는 묻지 마세요). – 나는 JSL을 작성하지 않았습니다 – 나는 단지 물건을 사용하지만 기본적으로 모두 이 두 가지의 변형일 뿐이며 동일한 작업을 수행합니다. JMP의 스크립팅 인덱스(도움말 > 스크립팅 인덱스)는 어떤 것이 어떤 GUI 요소와 함께 사용되는지 바로잡을 수 있는 좋은 방법입니다.
따라서 내 AA 기반 솔루션은 여러 가지 작업을 수행해야 합니다. 첫째, 내가 사용할 수 있어야 한다 << get() 그리고 << 세트() 모든 순열에서. 어떤 GUI 요소를 저장하고 싶은지 알려줄 수 있어야 합니다. 그리고 추가 기능 실행이 완료되어도 사라지지 않는 이 정보를 저장할 위치가 있어야 합니다. 즉, 수정된 작업 흐름에는 복원하려는 GUI 요소 목록을 사용하여 AA를 구축하는 작업이 포함됩니다. 이 AA를 전역 또는 일부 사용자 지정 네임스페이스에 넣어야 합니다(자세한 내용은 잠시 후에 설명하겠습니다). AA를 구성하는 것에 대해 생각하면서 "GUI 요소를 AA의 키로 사용한다면 키-값 쌍의 값으로 무엇을 사용해야 할까요?"라는 질문이 생겼습니다. AA가 작동하는 방식 때문에 AA를 비워 둘 수 있다는 것을 깨달았습니다. 즉, " 비어 있는() "를 내 가치관으로 삼았는데, 어째서인지 기회낭비처럼 느껴졌다. 이에 대해 좀 더 생각하면서 GUI 요소의 기본값을 AA 키-값 쌍에 넣으면 실제로 "재설정" 기능을 설정할 수 있다는 것을 깨달았습니다(GUI를 기본값으로 복원함). 상태) 동일한 코드를 사용합니다! 하나의 가격으로 두 가지 기능을 지원할 수 있습니다! 그리고 좋은 두 사람을 좋아하지 않는 사람은 누구입니까?
예, 네임스페이스가 유용합니다
문제로 돌아가서 네임스페이스 잠시 동안. 초보자들에게 네임스페이스는 다음과 같은 신비롭고 불가사의한 것처럼 보일 수 있습니다. JMP 디스커버리 서밋 아니면 가끔 블로그 포스팅에 지금 바로 그 거품을 터뜨리겠습니다. 그것들은 그다지 특별하지 않습니다. 그러나 이는 매우 유용하며 최고의 JSL 스크립터는 이를 효과적으로 사용하는 방법을 배웠습니다. (분명히 말씀드리자면 저는 그 그룹에 포함되지는 않지만 SAS HQ나 Discovery Summit에서 가끔 그들과 함께 점심을 먹습니다. 그래서 아마도 그게 중요한 의미가 있을 것입니다.)
네임스페이스가 무엇이고 무엇을 하는지에 대한 그림을 제공하기 위해 사전을 살펴보겠습니다. 당신이 뭔가를 쓰고 있다면 영어 단어를 찾아야 한다면 영어 사전. 만약에 일을 시작하게 된다면 프랑스 국민 단어를 찾아야 한다면 프랑스 국민 사전. 때로는 1066년 노르만 정복 덕분에 동일한 단어가 두 사전에 모두 나타날 수 있지만 의미가 약간 다를 수 있습니다. 문서가 있으므로 일반적으로 문제가 되지 않습니다. 문맥 당신을 돕기 위해. 당신이 작업 중이라면 프랑스 국민 문서, 당신은 도달 프랑스 국민 사전 – 문제 없습니다. 같은 문서에 프랑스어와 영어가 섞여 있어도 문제가 되지 않습니다. 문맥 어떤 사전을 찾아야 할지 알아내는 데 도움이 되는 단서입니다.이것이 JSL과 어떤 관련이 있나요? 네임스페이스는 사전과 같습니다. JMP에 원하는 작업을 이해할 수 있도록 추가 컨텍스트를 제공하는 사용자 정의 변수와 함수를 넣을 수 있는 곳입니다. 예를 들어 STK에서는 함수 이름을 사용합니다. 주석창() 여러 곳에서. 사실 저는 기존 창에 무언가를 그리는 소프트웨어의 모든 부분에서 이 이름의 함수를 사용합니다.전체적으로, 나는 아마도 6개의 함수를 가지고 있을 것입니다. 주석창() . 문제는 각 버전마다 약간씩 다른 기능을 수행한다는 것입니다. 그러나 각각은 고유한 네임스페이스에 존재하므로 이는 결코 문제가 되지 않습니다. 두 개의 다른 GUI가 동시에 열려 있더라도 마찬가지입니다. JMP는 내가 하나의 네임스페이스에서 작업할 때 기대하는 바가 무엇인지 알고 있습니다. 주석창() 한 가지 일을 하고 다른 일을 하면 그 일이 다른 일을 하기를 기대합니다. 이 전략은 STK의 GUI 설계 프로세스를 크게 단순화했습니다. 따라서 네임스페이스를 사용하면 다음을 제공할 수 있습니다. 문맥 JMP가 특정 스크립트에서 사용하려는 함수의 버전을 알 수 있도록 합니다. 해당 항목을 만든 추가 기능이 닫힌 후에도 메모리에 존재할 수도 있습니다. Recall 솔루션에 특히 유용한 것은 바로 이 두 가지 사항입니다.
이제 코딩할 시간이다
자, 이론은 충분합니다. 코드를 해킹해 보겠습니다. 게다가, 이것의 나머지 부분은 내가 진행하면서 설명하면 더 이해가 될 것입니다. 첫째, 저는 스크립트에 라이브러리를 사용하는 것을 옹호합니다. 이는 대부분의 개발 상황에서 모범 사례입니다. 대부분의 프로그래밍 언어가 이를 수행할 수 있습니다. JSL도 그렇게 할 수 있습니다. 그래서 나의 Recall 솔루션은 라이브러리 형태로 구축될 것입니다. 애플리케이션이나 스크립트에서 라이브러리를 사용하려면 개발자는 단지 포함하다() 라이브러리에 대한 파일 경로가 있는 스크립트 시작 부분의 인수입니다.
둘째, 저는 채팅 기능을 좋아합니다. 스크립트가 모든 단계에서 수행하는 작업을 알려주는 것은 정말 편리합니다. 하지만 나는 또한 내 채팅 기능을 다른 사람에게 알려줄 수 있기를 원합니다. 닥쳐 테스트와 코딩이 끝나면. (조심하지 않으면 수다스러운 기능으로 인해 코드 실행 속도가 느려질 수 있습니다.) 당신이 보면 실제 도서관 파일 교환에서는 많은 쓰기를 볼 수 있습니다 () 그리고 보여주다() 인수. 이것들은 If(상세 == 1, <…>) 명령을 전달하여 끌 수 있는 명령문 자세한 = 0 함수에 명령을 내립니다. 나는 스크립트에서 이 구성을 많이 사용하며 일반적으로 말 수가 많은 하나의 매개변수를 조정하여 전체 스크립트에 대해 자세한 로깅을 켜거나 끌 수 있도록 스크립트 상단에 변수를 추가합니다. 여기에서는 코드를 짧게 유지하기 위해 아래 스니펫에서 해당 코드를 제거했습니다. 완성된 라이브러리는 다음에서 찾을 수 있습니다. 함수 라이브러리 호출 .
라이브러리의 첫 번째 함수는 다음과 같습니다. 생성 배열() . 이 함수는 GUI의 요소 목록과 해당 기본값 목록에서 AA를 만듭니다. 이 기능은 실제로 AA의 복사본 두 개를 만듭니다. 하나는 Recall 기능을 지원하기 위한 것입니다. ::<플랫폼 이름>사용자 설정 , 그리고 재설정 기능을 지원하는 하나는 ::<플랫폼 이름>기본값 . 이 두 배열은 전역 네임스페이스에 저장됩니다. “ :: ” 변수 이름 앞에 있는 연산자가 이를 수행합니다. 주목해야 할 또 다른 점은 내가 평가(구문 분석()) 구조. 이는 대체 체계를 사용하면 번거로울 수 있는 동적 JSL을 구축하기 위한 일반적인 전략입니다. 마지막으로 주목해야 할 점은 내가 잠그다 사용자 설정 변수. 이는 AA 또는 값이 실수로 덮어쓰이는 것을 방지하기 위한 것입니다.
genArrays = Function( {objects, values, platformName, verbose = 1},
{Default Local},
// Create the defaults Array
defaultString = "::" || platformName || "Defaults = Associative Array( objects, values, Empty() )";
defaultStringName = "::" || platformName || "Defaults";
// Run the default command
Eval( Parse( defaultString ) );
// Create the defaults Array
userString = "::" || platformName || "UserSettings = ::" || platformName || "Defaults";
userStringName = "::" || platformName || "UserSettings";
// Try to create the user settings Array and lock it so it's not overwritten.
Try(
Eval( Parse( userString ) );
Eval( Parse( "Lock Globals( " || platformName || "UserSettings )" ) );
,
Write(
"\!N\!NThe Associative Array \!"" || platformName || "UserSettings\!" is already present in memory and ready for use.\!N\!N"
)
);
);
잠시 시간을 내어 AA를 구성하는 데 사용되는 목록에 무엇이 들어가는지 살펴보겠습니다. 이 문제에 접근하는 방법을 모색할 때 여러 가지 맛이 있다는 문제를 다루어야 했습니다. << get() 그리고 << 세트() 내가 처리해야 할 주장. 내가 생각해낸 것은 AA의 키-값 쌍의 키 부분에 헤더를 추가하는 것이었습니다. 이는 키(GUI 요소) 앞에 표시되는 하나 또는 두 개의 문자입니다. 다음을 사용하는 GUI 요소의 경우 << 세트() 헤더는 에^ . 그만큼 << 세트상품() 그리고 << 텍스트 설정() 사용 시^ 그리고 세인트^ 각기. GUI가 있는 경우(우리는 이를 “ 깔끔한GUI ”) 텍스트 상자( TB1 ), 숫자 상자( nb1 ) 및 목록 상자( lb1 ) 개체 목록은 다음과 같습니다.
objects = {st^tb1, s^nb1, si^lb1};
스크립트가 실행되거나 사용자가 재설정 버튼을 누를 때 특정 값을 갖기를 원한다고 말하면 가치 우리가 원하는 것을 나열하십시오. 나는 비어 있는() 목록 상자의 내용을 지우는 명령입니다.
values = {“hello world”, 42, Empty()};
이제 두 개의 목록이 있으므로 다음을 사용할 수 있습니다. GenArray() 다음과 같은 배열을 생성하는 함수:
GenArrays(objects, values, “neatGUI”);
그러면 전역 네임스페이스에 두 개의 AA가 생성됩니다. 생성 시 둘 다 다음과 같습니다.
[s^nb1=>42, si^lb1=>Empty(), st^tb1=>”hello world”]
이제 우리가 해야 할 일은 동일한 GUI 이름을 가진 다음 세 가지 함수를 제공하는 것입니다( 깔끔한GUI ) 그러면 나머지는 알아서 처리됩니다! 그들이 무엇을 하는지 살펴봅시다.
실제로 GUI와 상호작용하는 첫 번째 기능은 다음과 같습니다. 재설정역할() . 이 함수는 기본적으로 기본 AA의 각 키와 값을 가져오고 해당 정보를 사용하여 GUI 요소를 기본값으로 재설정하는 루프입니다.AA는 "특별한" 목록이므로 작업하려면 다른 지침을 사용해야 합니다. 따라서 아래 첨자를 사용하여 AA의 첫 번째 값을 얻는 대신 다음을 사용합니다. << 첫 번째 메시지. 여기부터는 키의 GUI 요소에서 헤더 부분을 분리하는 것뿐입니다.이 조각들은 일부 로직에 전달되므로 오른쪽을 사용합니다. << 세트() 현재 키에 대한 AA의 값이 포함된 메시지입니다. 마지막으로 이것은 모두 다른 것을 사용하여 실행됩니다. 평가(구문 분석()) 논쟁. 그런 다음 AA의 각 키-값 쌍에 대해 전체 프로세스가 반복됩니다.
resetRoles = Function( {platformName, verbose = 1},
{Default Local},
// Get the AA for the provided name
arrayName = Eval( Parse( "::" || platformName || "Defaults" ) );
arrayNameString = "::" || platformName || "UserSettings";
// Setup a logical break if an AA isn't provided
Try(
// Start the function
// Check if arrayName is an AA
If( Is Associative Array( arrayName ),
//Initialize the counter
currentKey = arrayName << First;
// Loop through the keys
For( i = 1, i <= N Items( arrayName ), i++,
// Extract the header and the key
stAA = currentkey; // The header-key pair
stType = Word( 1, stAA, "^" ); // The header part
stArg = Word( 2, stAA, "^" ); // The key part
// Match the operation header to the correct operation instruction and build the string
stArgString = Match( stType,
"si", stArg || " << Set Items( " || Char( arrayName[currentkey] ) || " )",
"s", stArg || " << Set( " || Char( arrayName[currentkey] ) || " )",
"st", stArg || " << Set Text( \!"" || Char( arrayName[currentkey] ) || "\!" )"
);
// Eval-Parse the string
Try( Eval( Parse( stArgString ) ) );
// Get the next key in the AA
nextkey = arrayName << Next( currentKey );
// set it as the target key for the next loop
currentkey = nextkey;
);
,
// Stop execution if not an AA.
Throw()
);
,
);
);
다른 두 가지 기능은 저장소 역할() 그리고 리콜롤() , 몇 가지 작은 차이점을 제외하고 비슷한 방식으로 작업합니다. 첫째, 둘 다 다음에서 작동합니다. 사용자 대신에 AA 기본 GenArrays가 생성한 AA () . 두번째, 저장소 역할() 용도 << get() 메시지를 덮어쓰고 값 GUI 요소에 대한 키-값 쌍의 일부이므로 나중에 다시 불러올 수 있도록 저장됩니다. 편집 중에 사용자 AA를 잠금 해제하고 다시 잠그는 일부 코드도 포함되어 있습니다. 마지막으로 리콜Roles () 함수는 << 추가() 메시지를 시^ 헤더 대신 << 세트() 메시지. 여기에 코드를 입력하는 대신 파일 교환의 라이브러리 .
그래서 약간의 생각, 코딩, 삭제 키의 자유분방한 적용(Windows 세계에 갇힌 사람들을 위한 백스페이스... 애도), 그리고 조금 더 코딩을 한 후에 Recall/Reset을 지원하는 일련의 기능을 구축하는 것이 전적으로 가능하다는 것을 알게 되었습니다. 연관 배열을 사용합니다. 돌이켜보면 요소가 두 개 또는 세 개 있는 간단한 GUI의 경우 이 전체 프로세스는 분명히 약간의 작업입니다. 루브-골드버그풍 . 하지만 10개 또는 20개의 요소가 있는 GUI를 상상해 보세요. 이 접근 방식은 기존 접근 방식보다 확장성이 훨씬 뛰어납니다. 호출/재설정하려는 항목과 해당 초기 값 목록만 추적하면 됩니다. 그리고 이를 위해 무거운 오스트리아 억양을 사용하거나 화성을 통제하기 위해 권력에 굶주린 주지사와 싸울 필요조차 없습니다.