Aby odpowiedzieć na twoje pytanie: prototyp call()
w instrukcji jest call({func}, {arglist} [, {dict}])
; {arglist}
argumentem musi być dosłownie obiekt List, a nie lista argumentów. Oznacza to, że musisz to napisać w ten sposób:
let @x = call(a:functionToExecute, [GetSelectedText()])
Zakłada się, że a:functionToExecute
jest to Funcref (patrz :help Funcref
) lub nazwa funkcji (tj. Ciąg znaków, taki jak 'Type1ProcessString'
).
To potężna funkcja, która daje Vimowi jakość przypominającą LISP, ale prawdopodobnie rzadko używałbyś go jak wyżej. Jeśli a:functionToExecute
jest ciągiem, nazwą funkcji, możesz to zrobić:
function! Wrapper(functionToExecute)
" ...
let s:processing = function(a:functionToExecute)
let @x = s:processing(GetSelectedText())
" ...
endfunction
i wywołałbyś opakowanie z nazwą funkcji:
call Wrapper('Type1ProcessString')
Jeśli z drugiej strony a:functionToExecute
jest Funcref, możesz wywołać go bezpośrednio:
function! Wrapper(functionToExecute)
" ...
let @x = a:functionToExecute(GetSelectedText())
" ...
endfunction
ale musisz wywołać opakowanie w ten sposób:
call Wrapper(function('Type1ProcessString'))
Możesz sprawdzić istnienie funkcji za pomocą exists('*name')
. Umożliwia to następującą małą sztuczkę:
let s:width = function(exists('*strwidth') ? 'strwidth' : 'strlen')
tj. funkcja, która korzysta z wbudowanego, strwidth()
jeśli Vim jest wystarczająco nowy, aby go mieć, i wraca do strlen()
innych (nie twierdzę, że taka awaria ma sens; mówię tylko, że można to zrobić). :)
Za pomocą funkcji słownika (patrz :help Dictionary-function
) możesz zdefiniować coś przypominającego klasy:
let g:MyClass = {}
function! g:MyClass.New(...)
let newObj = copy(self)
if a:0 && type(a:1) == type({})
let newObj._attributes = deepcopy(a:1)
endif
if exists('*MyClassProcess')
let newObj._process = function('MyClassProcess')
else
let newObj._process = function('s:_process_default')
endif
return newObj
endfunction
function! g:MyClass.getFoo() dict
return get(get(self, '_attributes', {}), 'foo')
endfunction
function! g:MyClass.setFoo(val) dict
if !has_key(self, '_attributes')
let self._attributes = {}
endif
let self._attributes['foo'] = a:val
endfunction
function! g:MyClass.process() dict
call self._process()
endfunction
function! s:_process_default()
echomsg 'nothing to see here, define MyClassProcess() to make me interesting'
endfunction
Następnie utworzyłbyś takie obiekty:
let little_object = g:MyClass.New({'foo': 'bar'})
I nazwij jej metody:
call little_object.setFoo('baz')
echomsg little_object.getFoo()
call little_object.process()
Możesz także mieć atrybuty i metody klas:
let g:MyClass.__meaning_of_life = 42
function g:MyClass.GetMeaningOfLife()
return get(g:MyClass, '__meaning_of_life')
endfunction
(zauważ, że nie ma dict
tu potrzeby ).
Edycja: Podklasowanie jest mniej więcej takie:
let g:MySubclass = copy(g:MyClass)
call extend(g:MySubclass, subclass_attributes)
Subtelnym punktem tutaj jest użycie copy()
zamiast deepcopy()
. Powodem tego jest dostęp do atrybutów klasy nadrzędnej przez odniesienie. Można to osiągnąć, ale jest to bardzo kruche, a poprawne wykonanie go wcale nie jest trywialne. Innym potencjalnym problemem jest to, że tego rodzaju zrównują podklasy is-a
z has-a
. Z tego powodu atrybuty klasy zwykle nie są tak naprawdę warte bólu.
Ok, to powinno wystarczyć, aby dać ci trochę do myślenia.
Wracając do początkowego fragmentu kodu, można go ulepszyć w dwóch szczegółach:
- nie musisz
normal gvd
usuwać starego wyboru, normal "xp
zastąpi go, nawet jeśli nie zabijesz go jako pierwszy
- użyj
call setreg('x', [lines], type)
zamiast let @x = [lines]
. To wyraźnie określa typ rejestru x
. W przeciwnym razie polegasz na tym, że masz x
już prawidłowy typ (tj. Znakowy, liniowy lub blokowy).
dict
słowa kluczowego. Dotyczy to twoich „metod klasowych”. Zobaczyć:h numbered-function
.