Napisz funkcję (nie pełny program), aby jeśli funkcja została wywołana z jedną zmienną globalną (lub najbliższym odpowiednikiem twojego języka) jako argumentem, wypisuje (tj. Wypisuje lub zwraca) nazwę tej zmiennej. Jeśli argument nie jest zmienną, zamiast tego wypisz wartość falsey. (Nie musisz zajmować się przypadkiem, w którym argument jest zmienną, ale nie globalną.)
Pomiędzy wywołaniem funkcji a definicją funkcji nie może istnieć połączenie w czasie kompilacji (w szczególności definicja funkcji nie może być makrem lub podobną konstrukcją, która odbiera argumenty w postaci dosłownego fragmentu kodu źródłowego, ani w tekście, ani w abstrakcyjnym drzewie składni Formularz). To znaczy: w językach, które obsługują oddzielną kompilację, program musi działać, nawet jeśli wywołanie funkcji jest najpierw kompilowane (bez znajomości definicji funkcji, ale możliwe, że jest to podpis typu lub równoważny), a następnie definicja funkcji jest następnie kompilowana. Jeśli język nie ma osobnej kompilacji, musisz jednak dążyć do podobnej separacji między wywołaniem a definicją.
Jeśli używasz skompilowanego języka, możesz odczytać skompilowaną formę całego programu z dysku i możesz założyć, że program został skompilowany z informacjami debugowania. (W ten sposób dozwolone są rozwiązania, które działają poprzez dołączenie debuggera z programu do siebie.)
Pamiętaj, że to zadanie może nie być możliwe w każdym języku.
Przykłady:
var apple = "Hello"
p(apple) // apple
var nil = "Nothing"
p(nil) // nil
var SOMETHING = 69
p(SOMETHING) // SOMETHING
function foo(){}
p(foo) // foo
p(3.14159) // false
p(function foo(){}) // false
źródło
Odpowiedzi:
Jawa
Obecnie działa to z kilkoma gotchas:
javac
z-g
flagą. Generuje to wszystkie informacje debugowania, w tym nazwy zmiennych lokalnych w skompilowanym pliku klasy.com.sun.tools.javap
który analizuje kod bajtowy pliku klasy i daje wynik czytelny dla człowieka. Ten interfejs API jest dostępny tylko w bibliotekach JDK, więc musisz użyć środowiska wykonawczego JDK java lub dodać tools.jar do ścieżki klasy.To powinno teraz działać, nawet jeśli metoda jest wywoływana wielokrotnie w programie. Niestety to nie działa, jeśli masz wiele wywołań w jednym wierszu. (Dla takiego, patrz poniżej)
Wypróbuj online!
Wyjaśnienie
Ta pierwsza część zawiera ogólne informacje o tym, w jakiej klasie się znajdujemy i jak nazywa się ta funkcja. Można to osiągnąć, tworząc wyjątek i analizując pierwsze 2 wpisy śledzenia stosu.
Pierwszy wpis to wiersz, w którym zgłaszany jest wyjątek, z którego możemy pobrać metodę methodName, a drugi wpis to miejsce, z którego wywołano funkcję.
W tym wierszu wykonujemy plik wykonywalny javap, który jest dostarczany z JDK. Ten program analizuje plik klasy (kod bajtowy) i przedstawia wynik czytelny dla człowieka. Wykorzystamy to do podstawowego „parsowania”.
Robimy tutaj kilka różnych rzeczy. Po pierwsze, czytamy wynik wyjściowy javap linia po linii do listy. Po drugie, tworzymy mapę indeksów linii kodu bajtowego na indeksy linii javap. To pomaga nam później ustalić, które wywołanie metody chcemy przeanalizować. Na koniec używamy znanego numeru linii ze śladu stosu, aby określić, który indeks linii kodu bajtowego chcemy oglądać.
Tutaj iterujemy jeszcze raz po wierszach javap, aby znaleźć miejsce, w którym wywoływana jest nasza metoda i gdzie zaczyna się lokalna tabela zmiennych. Potrzebujemy linii, w której metoda jest wywoływana, ponieważ linia przed nią zawiera wywołanie do załadowania zmiennej i identyfikuje, którą zmienną (według indeksu) załadować. Tabela zmiennych lokalnych pomaga nam właściwie wyszukać nazwę zmiennej na podstawie przechwyconego przez nas indeksu.
Ta część analizuje wywołanie load, aby uzyskać indeks zmiennej. Może to spowodować wyjątek, jeśli funkcja nie jest faktycznie wywoływana ze zmienną, dzięki czemu możemy tutaj zwrócić wartość null.
Na koniec analizujemy nazwę zmiennej z wiersza w lokalnej tabeli zmiennych. Zwróć null, jeśli nie zostanie znaleziony, chociaż nie widziałem powodu, dla którego miałoby się tak stać.
Kładąc wszystko razem
Właśnie na to patrzymy. W przykładowym kodzie pierwsze wywołanie to linia 17. linia 17 w LineNumberTable pokazuje, że początek tej linii to indeks kodu linii bajtowej 18. To jest
System.out
obciążenie. Mamy więcaload_2
tuż przed wywołaniem metody, więc szukamy zmiennej w gnieździe 2 tabeli LocalVariableTable, która jeststr
w tym przypadku.Dla zabawy, oto jedno, które obsługuje wiele wywołań funkcji na tej samej linii. To powoduje, że funkcja nie jest idempotentna, ale o to właśnie chodzi. Wypróbuj online!
źródło
System.out.println(e.getParamName(str));System.out.println(e.getParamName(str2));System.out.println(e.getParamName(str4));
na jedna linia w łączu TIO.javap
lokalizację takiego:Paths.get(System.getProperty("java.home"), "bin", "javap")
.Python 2
Chodzi o najbardziej brudny kod, który napisałem, ale działa. ¯ \ _ (ツ) _ / ¯ Zgłasza błąd w nieistniejącej zmiennej, ponieważ Python od razu cię nie polubi za wywołanie tej funkcji. Zgłasza również błąd dla zmiennych, ale można to naprawić za pomocą try / z wyjątkiem potrzeby.
Wypróbuj online!
Jeśli wolno nam traktować argument jako ciąg znaków, spełnia to wymagania wypisania wartości fałszowania przy nieprawidłowym wejściu.
Wypróbuj online!
źródło
Matematyka
HoldFirst
Cecha uniemożliwiaf
od oceny jej argumentu przed wywołaniem funkcji.ValueQ @ x
następnie sprawdza, czy podany argument jest zmienną, której nadano wartość. Jeśli nie, po prostu wrócimy zFalse
powodu zwarcia. W przeciwnym razie otrzymujemy nazwę zmiennej za pomocąToString @ HoldForm @ x
.źródło
And
... Podoba mi się, że właściwy argumentAnd
nie musi być nawet logicznym wyrażeniem.f[x_] := ValueQ @ x && HoldForm @ x
? Gdyby nie wymóg sprawdzania, czy dane wejściowe są zmienne,HoldForm
sam by działał.HoldAll
zamiastHoldFirst
?HoldForm[a]
i nie"a"
. Chociaż są one wyświetlane tak samo w zeszycie Mathematica, funkcja istnieje niezależnie od interfejsu użytkownika, więc chciałem mieć rozwiązanie zwracające ciąg znaków.Matematyka
Mathematica lubi wszystko oceniać , więc aby się zatrzymać, musimy z tym walczyć. We własnym języku.
W jaki sposób?
Mówi Mathematica, że za każdym razem, gdy
f
wywoływana jest funkcja , jej argumenty nie powinny być oceniane (tzn. Wstrzymywane).Kiedy
f
jest wywoływany z argumentem, wyjścieFalse
. (?!)Gdy
f
zostanie wywołany ze zdefiniowanym Symbolem (który ma wartość), wypisz nazwę symbolu wejścia.Poprzedni wiersz nie ma pierwszeństwa, mimo że został wcześniej zdefiniowany, ponieważ ta definicja jest bardziej szczegółowa. Jak stwierdza Dokumentacja Wolfram: Mathematica „próbuje umieścić określone definicje przed bardziej ogólnymi definicjami”.
Mathematica jest bardzo uparta i stara się oceniać zmienne, gdy tylko jest to możliwe. Dodatkowy
Unevaluated
dba o to.źródło
DO#
Wersja pełna / sformatowana:
Innym sposobem na to jest zastanowienie się nad typem:
Musisz to jednak nazwać tak:
Następnie również nie powiedzie
3.14159
się, powracając,Length
a do tego musisz również wywołać go w następujący sposób:W C # 6.0 mamy również:
Ale to się nie skompiluje, jeśli przekażesz coś, co nie jest zmienną, np
3.14159
. Wierzę, że ocenia również dane wejściowe w czasie kompilacji, więc wydaje się, że nie spełnia również tego wymagania.źródło
Expression
). Powinieneś także liczyćusing System.Linq.Expressions;
.MATLAB
W ramach funkcji znajduje się kilka predefiniowanych zmiennych, takich jak
varargin
lubnargin
, wśród tych, które również mamyinputname
.skradziony stąd
źródło
x=42;y=54; f=@(x)eval(inputname(1));f(x),f(y)
eval==evil
= D (faktycznie współpracujex=42;y=54; f=@(x)eval('inputname(1)');f(x),f(y)
)x
, a następnie sprawdzaeval
argument funkcji, a nie zmienną obszaru roboczego.R
substitute
zwraca drzewo parsowania dla nieocenionego wyrażenia. Teidentical
marki warunkowe pewien, że ten unevaluated ekspresja nie jest identyczny do samego wyrazu; tzn. że przekazany parametr nie jest literałem.źródło
Python 2
Niektóre badania zostały wykonane. I wydaje się, że jest to możliwe w Pythonie, ale nadal spodziewam się problemów.
Rozwiązanie nie jest idealne, ponieważ zakłada pewną strukturę kodu. Jeszcze go nie złamałem.
Używa inspekcji, aby spojrzeć na zasięg przestrzenny i znaleźć, gdzie
get_name
wywoływana jest funkcja . Na przykładinspect...[1]
wróciI
inspect...[1][4]
pokaże nam listę z kodem, w którym wywoływana jest funkcja:Następnie użyłem wyrażenia regularnego, aby pobrać nazwę argumentu
I
.lstrip().rstrip()
aby usunąć wszystkie białe spacje, które mogą być umieszczone w bransoletach.Przykład z trudnymi danymi wejściowymi
źródło
PowerShell
Ale to nie działa niezawodnie, jest szczątkowe.
źródło
PHP
Wymaga, aby wartość zmiennej była unikalna w tablicy zmiennych globalnych
Wypróbuj online!
PHP , 96 bajtów
Wypróbuj online!
Wymaga zainicjowania zmiennej bezpośrednio w wywołaniu funkcji.
Sprawdza, czy ostatnia zmienna globalna jest równą zmienną poddającą się
źródło
JavaScript (ES6)
Dla wszystkich nazw właściwości w
window
(Object.getOwnPropertyNames(w)
) spróbuj zdefiniować moduł pobierający dla tej właściwości, która zwraca nazwę właściwości.Następnie dodaj wpis do mapy
M
którym klucz jest (prawdopodobnie przesłoniętą) wartością właściwości, a wartość jest nazwą właściwości.Funkcja
f
po prostu pobiera zmienną (tj. Właściwośćwindow
) i zwraca wpisM
dla tej wartości.Działa to dla wszystkich wbudowanych zmiennych globalnych z wyjątkiem
window
siebie, ponieważ nie ma sposobu, aby je odróżnićtop
(chyba, że jest uruchamiany w ramce):źródło
L not a function
. Dlaczego miałby to się stało?Perl 5 + Devel :: Caller
Używamy Devel :: Caller (moduł podobny do debuggera) do przejścia przez stos wywołań, szukając wywołania funkcji i zwracając wszystkie argumenty argumentu, zwracając je jako nazwy zmiennych. Jeśli jest więcej (lub mniej) niż jeden operand, nie jesteśmy wywoływani ze zmienną. Jeśli operand nie był zmienną, nie będzie miał nazwy i my też możemy to wykryć.
Najtrudniejszy jest przypadek, gdy otrzymamy wyrażenie z jednym argumentem zawierające zmienną, na przykład
~$x
. Możemy dowiedzieć się, czy tak się stało, odwołując się do zmiennej bezpośrednio z tabeli symboli (używając${…}
składni symbolicznego odwołania) i porównując jej adres pamięci z wartością, którą przekazaliśmy jako argument (który dogodnie jest przekazywany przez odwołanie ). Jeśli są różne, mamy wyrażenie zamiast pojedynczej zmiennej.Zauważ, że jeśli wywołamy tę funkcję z wyrażeniem wstępnym lub wstępnym na jednej zmiennej, tak jak w
v(--$x)
, otrzymamy zwrot$x
. Jest tak, ponieważ w rzeczywistości w tym przypadku przekazywana jest funkcja. po prostu zwiększa się lub zmniejsza wcześniej. Mam nadzieję, że to nie dyskwalifikuje odpowiedzi. (W pewnym sensie poprawia to, ponieważ pokazuje, że sprawdzamy sam argument, a nie tylko czytamy kod źródłowy.)źródło
PHP
Podczas gdy inne zgłoszenia PHP sprawdzają tylko, czy podana wartość odpowiada wartości globalnej, ta wersja działa, odwołując się do wartości:
To powinno teraz działać, nawet jeśli zmienna globalna jest odniesieniem do innej wartości w momencie wywołania.
Sprawdź to tutaj .
źródło
Röda
Wypróbuj online!
Röda ma do tego wbudowaną funkcję -
name
. Niestety nie zwraca wartości falsy, gdy nie jest podana zmienna.Ten kod narusza kilka funkcji semantyki odniesienia. Objaśnienie linia po linii:
f(&a...) {
Funkcja przyjmuje zmienną liczbę argumentów referencyjnych (
&a...
). Jest to jedyny sposób, aby w Röda utworzyć listę referencji.a() | name(_) | for str do
W drugiej linii elementy
a
są wypychane do strumienia (a()
). Te elementy są referencjami, jeśli funkcja otrzyma zmienne, a w przeciwnym razie wartości normalne.Elementy są iterowane za pomocą pętli podkreślenia. Podkreśleniem jest składnia cukru dla
for
pętli, więcname(_)
jest równoważna zname(var) for var
. Nazwa zmiennej używanej wfor
pętli ma postać, w<sfvN>
której N się zmienia. (Tj.)name(<sfv1>) for <sfv1>
”) Zmienna zdefiniowana przez użytkownika zawiera<
lub>
, więc wygenerowane nazwy nie kolidują z istniejącymi nazwami zmiennych.name()
Funkcja zwraca nazwę danej zmiennej. Jeślia
iterowany element jest odwołaniem, to jest to zmienna podana doname
. W przeciwnym razie, jeśli element miałby wartość normalną, podana zmiennaname
jest zmienną podkreślenia<sfvN>
. Wynika to z semantyki odniesień w Röda: jeśli funkcja przyjmuje odwołanie, a funkcja jest przekazywana do zmiennej odniesienia, przekazywana wartość nie wskazuje na zmienną odniesienia, ale na zmienną, na którą wskazuje zmienna odniesienia.false if [ "<" in str ] else [str]
W trzecim wierszu sprawdzamy, czy nazwa zmiennej w strumieniu zawiera
<
znak. Jeśli tak, jest to zmienna podkreślenia, a przekazana wartośćf
nie była odwołaniem. W przeciwnym razie wypisujemy nazwę referencji.To rozwiązanie nie działa, jeśli zmienna podana do funkcji jest zmienną odniesienia lub podkreślenia. Jednak pytanie określa, że należy obsługiwać tylko zmienne globalne i nie mogą one być odwołaniami ani zmiennymi podkreślającymi w Rödzie.
źródło
Rubin , 46 bajtów
Czuję się jak najbrudniejszy kod, jaki kiedykolwiek napisałem dla Ruby.
Wymaga, aby wywoływane zmienne globalne miały unikalną wartość, która nie występuje w żadnej innej zmiennej globalnej, ponieważ jedynym sposobem na wykonanie tego wyzwania w Ruby jest przeszukanie wszystkich zmiennych globalnych i zwrócenie pierwszego dopasowania. OP twierdzi, że jest w porządku i może ocenić, czy moje rozwiązanie jest prawidłowe.
Zauważ, że zmienne globalne zaczynają się od
$
w Ruby, bo jeśli chcesz dodać dodatkowe rzeczy z przypadków testowych.Wypróbuj online!
źródło
PHP
jeśli wartość zostanie znaleziona, wypisz nazwę zmiennej i zakończ. nic nie drukuj i nie wychodź inaczej.
61 bajtów, aby zwrócić nazwę zmiennej lub
NULL
:Nie znajdzie funkcji nazwanych, tylko te przypisane do zmiennych.
A funkcja PHP nie może wykryć, czy parametr został podany przez referencję lub wartość. Funkcja zwróci tylko imię, w którym wartość odpowiada wartości parametru funkcji.
Przetestuj online
źródło
PowerShell
Nowa wersja, ale działa począwszy od PowerShell 3.0
Wypróbuj online!
Poprzednia wersja
Wypróbuj online!
źródło
tcl
próbny
źródło
JavaScript (ES6)
Wymaga, aby wartość zmiennej przekazywanej do funkcji była unikalna dla tej zmiennej. Zwraca,
undefined
jeśli zmienna nie została przekazana.Spróbuj
Z jakiegoś powodu po przekazaniu „literału” zgłasza błąd pochylenia we fragmencie.
Wyjaśnienie
Zapętlaj wszystkie wpisy w obiekcie globalnym (
this
), sprawdzając, czy wartość każdego z nich jest ściśle równa wartości argumentu przekazanego do funkcji. Jeśli zostanie znaleziony pasujący wpis, zwracany jest jego klucz (nazwa), wychodząc z funkcji.Alternatywny
Z tymi samymi wymaganiami jak powyżej
źródło
hello--
.). Musisz też użyćvar
zamiastlet
.let
ivar
. Na twoje drugie pytanie: Stało się tak, ponieważ masz zmienną nazwanąIN_GLOBAL_SCOPE
w swoim globalnym zasięgu i ma ona wartość1
. Możesz sprawdzić istniejące zmienne w swoim globalnym zasięgu i ich wartości, uruchamiając to przed przetestowaniem powyższego:for(x in this)console.log(x+": "+this[x])
Szybki, 45 bajtów
źródło
value of type 'Mirror' has no member 'label'