Czy w MATLAB można zdefiniować więcej niż jedną funkcję na plik i uzyskać do nich dostęp spoza tego pliku?

217

Kiedy uczyłem się na studiach licencjackich w EE, MATLAB wymagał, aby każda funkcja była zdefiniowana we własnym pliku, nawet jeśli była to jedna linijka.

Studiuję teraz na stopień magistra i muszę napisać projekt w MATLAB. Czy nadal jest to wymagane w przypadku nowszych wersji MATLAB?

Jeśli możliwe jest umieszczenie więcej niż jednej funkcji w pliku, czy istnieją jakieś ograniczenia? Na przykład, czy wszystkie funkcje w pliku są dostępne spoza pliku, czy tylko funkcja o takiej samej nazwie jak plik?

Uwaga: używam wersji MATLAB R2007b.

Nathan Fellman
źródło

Odpowiedzi:

271

Pierwsza funkcja w pliku m (tj. Funkcja główna ) jest wywoływana po wywołaniu tego pliku m. Nie jest wymagane, aby funkcja główna miała taką samą nazwę jak plik m, ale dla jasności powinna . Gdy funkcja i nazwa pliku różnią się, nazwa pliku musi być użyta do wywołania funkcji głównej.

Wszystkie kolejne funkcje w pliku m, zwane funkcjami lokalnymi (lub „podfunkcjami” w starszej terminologii), mogą być wywoływane tylko przez funkcję główną i inne funkcje lokalne w tym pliku m. Funkcje innych plików m nie mogą ich wywoływać. Począwszy od wersji R2016b, można również dodawać funkcje lokalne do skryptów , chociaż zachowanie zakresu jest nadal takie samo (tzn. Można je wywoływać tylko ze skryptu).

Ponadto możesz zadeklarować funkcje w ramach innych funkcji. Są to tak zwane funkcje zagnieżdżone i można je wywoływać tylko z funkcji, w której są zagnieżdżone. Mogą również mieć dostęp do zmiennych w funkcjach, w których są zagnieżdżone, co czyni je dość przydatnymi, choć nieco trudnymi w pracy.

Więcej jedzenia do przemyślenia ...

Istnieje kilka sposobów opisanych powyżej normalnego zachowania zakresu funkcji, takich jak przekazywanie uchwytów funkcji jako argumentów wyjściowych, jak wspomniano w odpowiedziach SCFrench i Jonasa (które, począwszy od R2013b, jest ułatwione przez localfunctionsfunkcję). Nie sugeruję jednak, aby nawyk polegał na stosowaniu takich sztuczek, ponieważ prawdopodobnie istnieją znacznie lepsze opcje organizowania funkcji i plików.

Na przykład, powiedzmy, że masz główną funkcję Aw m-pliku A.m, wraz z lokalnych funkcji D, Eoraz F. Teraz załóżmy, że masz dwie inne powiązane funkcje Bi Cw M-Files B.mi C.m, odpowiednio, że chcemy także, aby móc zadzwonić D, Ei F. Oto kilka opcji, które masz:

  • Umieść D, Ei Fkażdy w osobnych plikach m, umożliwiając wywoływanie ich przez dowolną inną funkcję. Minusem jest to, że zakres tych funkcji jest duża i nie ogranicza się tylko A, Bi C, ale Plusem jest to, że jest to całkiem proste.

  • Utwórz plik defineMyFunctionsm (jak w przykładzie Jonasa) za pomocą D, Eoraz Fjako funkcje lokalne i główna funkcja, która po prostu zwraca do nich uchwyty funkcji. Pozwala to zachować D, Ei Fw tym samym pliku, ale nie robi nic w odniesieniu do zakresu tych funkcji, ponieważ każda funkcja, która może zostać wywołana, defineMyFunctionsmoże je wywołać. Musisz także obawiać się o przekazanie uchwytów funkcji jako argumentów, aby upewnić się, że masz je tam, gdzie ich potrzebujesz.

  • Skopiować D, Ea Fdo B.mi C.mjako funkcje lokalnych. To ogranicza zakres ich wykorzystania do właśnie A, Bi C, ale czyni aktualizację i utrzymanie kodu koszmar bo masz trzy kopie tego samego kodu w różnych miejscach.

  • Korzystaj z funkcji prywatnych ! Jeśli masz A, Boraz Cw tym samym katalogu, można utworzyć podkatalog o nazwie privatei miejsce D, Ei Ftam, każdy jako oddzielny m-pliku. To ogranicza ich zakres, więc mogą być wywoływane tylko przez funkcje w katalogu natychmiast powyżej (tj A, Bi C) i utrzymuje je razem w tym samym miejscu (ale wciąż różne M-Files):

    myDirectory/
        A.m
        B.m
        C.m
        private/
            D.m
            E.m
            F.m

Wszystko to nieco wykracza poza zakres twojego pytania i jest prawdopodobnie bardziej szczegółowe niż potrzebujesz, ale pomyślałem, że dobrze byłoby poruszyć bardziej ogólną troskę o uporządkowanie wszystkich twoich m-plików. ;)

gnovice
źródło
3
Ulubiona opcja odpowiedzi wygląda następująco ^: @idigas
embert
1
@embert Zakładam, że miał na myśli faworyzowanie pytania, które można poddać pod głosowanie niezależnie od faworyzowania.
OJFord,
79

Zasadniczo odpowiedź na twoje pytanie brzmi: nie, nie możesz zdefiniować więcej niż jednej widocznej z zewnątrz funkcji na plik. Można jednak zwrócić uchwyty funkcji do funkcji lokalnych, a wygodnym sposobem na to jest utworzenie z nich pól struktury. Oto przykład:

function funs = makefuns
  funs.fun1=@fun1;
  funs.fun2=@fun2;
end

function y=fun1(x)
  y=x;
end

function z=fun2
  z=1;
end

A oto jak można go użyć:

>> myfuns = makefuns;
>> myfuns.fun1(5)    
ans =
     5
>> myfuns.fun2()     
ans =
     1
SCFrench
źródło
36

Jedynym sposobem na posiadanie wielu osobno dostępnych funkcji w jednym pliku jest zdefiniowanie METOD STATYCZNYCH przy użyciu programowania obiektowego . Chcesz uzyskać dostęp do funkcji jako myClass.static1(), myClass.static2()etc.

Funkcjonalność OOP jest oficjalnie obsługiwana od wersji R2008a, więc jeśli nie chcesz używać starej, nieudokumentowanej składni OOP, odpowiedź dla Ciebie jest przecząca , jak wyjaśnia @gnovice .

EDYTOWAĆ

Jeszcze jednym sposobem zdefiniowania wielu funkcji w pliku, które są dostępne z zewnątrz, jest utworzenie funkcji, która zwraca wiele uchwytów funkcji . Innymi słowy, wywołalibyśmy funkcję definiującą jako [fun1,fun2,fun3]=defineMyFunctions, po czym można użyć out1=fun1(inputs)itd.

Jonas
źródło
Nie użyłbym do tego celu oop, dodaje to znacznego obciążenia, szczególnie w przypadku metod statycznych. ( stackoverflow.com/questions/1693429/… )
Daniel
1
@Daniel: Narzut jest zauważalny tylko wtedy, gdy wykonujesz ogromną liczbę wywołań funkcji, a obliczenia w metodzie są prawie natychmiastowe. Oba warunki często wskazują na zły projekt - brak wektoryzacji i pozbawione znaczenia funkcje. Dlatego nie byłbym zbyt zmartwiony.
Jonas
23

Naprawdę podoba mi się odpowiedź SCFrench - chciałbym zaznaczyć, że można ją łatwo zmodyfikować, aby zaimportować funkcje bezpośrednio do obszaru roboczego za pomocą funkcji przypisania. (Robienie tego w ten sposób przypomina mi sposób robienia rzeczy przez Pythona „import x from y”)

function message = makefuns
  assignin('base','fun1',@fun1);
  assignin('base','fun2',@fun2);
  message='Done importing functions to workspace';
end

function y=fun1(x)
  y=x;
end

function z=fun2
  z=1;
end

A następnie wykorzystane w ten sposób:

>> makefuns
ans =
Done importing functions to workspace

>> fun1(123)
ans =
   123

>> fun2()
ans =
     1
Ru Hasha
źródło
assignin('caller',...)byłoby bardziej poprawne. Możesz użyć tych funkcji z innej funkcji.
Cris Luengo,
10

W tym samym wierszu co odpowiedź SCFrench, ale z bardziej spinem w stylu C #.

Zrobiłbym (i często robię) klasę zawierającą wiele metod statycznych. Na przykład:

classdef Statistics

    methods(Static)
        function val = MyMean(data)
            val = mean(data);
        end

        function val = MyStd(data)
            val = std(data);
        end
    end

end

Ponieważ metody są statyczne, nie trzeba inicjować klasy. Funkcje wywołuje się w następujący sposób:

data = 1:10;

mean = Statistics.MyMean(data);
std = Statistics.MyStd(data);     
SmallJoeMan
źródło
4

Definiuję wiele funkcji w jednym pliku .m za pomocą Octave, a następnie używam polecenia z pliku .m, w którym muszę skorzystać z funkcji z tego pliku:

source("mycode.m");

Nie jestem pewien, czy jest to dostępne w Matlabie.

octave:8> help source
'source' is a built-in function

 -- Built-in Function:  source (FILE)
     Parse and execute the contents of FILE.  This is equivalent to
     executing commands from a script file, but without requiring the
     file to be named `FILE.m'.
JD
źródło
3

Możesz również pogrupować funkcje w jednym pliku głównym razem z funkcją główną, która wygląda tak:

function [varargout] = main( subfun, varargin )
[varargout{1:nargout}] = feval( subfun, varargin{:} ); 

% paste your subfunctions below ....
function str=subfun1
str='hello'

Następnie wywołanie subfun1 wyglądałoby tak: str = main ('subfun1')

Thierry Dalon
źródło
0

Od wersji R2017b nie jest to oficjalnie możliwe. W stosownej dokumentacji stwierdza, że:

Pliki programów mogą zawierać wiele funkcji. Jeśli plik zawiera tylko definicje funkcji, pierwsza funkcja jest funkcją główną i jest funkcją, którą MATLAB kojarzy z nazwą pliku. Funkcje następujące po głównej funkcji lub kodzie skryptu nazywane są funkcjami lokalnymi. Funkcje lokalne są dostępne tylko w pliku.

Jednak obejścia sugerowane w innych odpowiedziach mogą osiągnąć coś podobnego.

Diabeł
źródło
Nie to dokładnie powiedział Gnovice na początku swojej odpowiedzi?
Adiel
@Adiel Być może, ale minęło kilka lat od tej odpowiedzi i ktoś może się zastanawiać, czy coś się zmieniło.
Dev-iL
Nadal nie dostałem, jeśli coś się zmieniło ...? :)
Adiel
Nie. Oprócz kilku dokumentów, które zostały dodane w celu rozwiązania tego konkretnego tematu.
Dev-iL
Powodem, dla którego napisałem tę odpowiedź, jest to, że kilka wydań temu wprowadziło funkcje, które można dodać na końcu skryptów - więc można się zastanawiać, czy coś się zmieniło również pod tym względem (odpowiedź: nie).
Dev-iL
-1

Próbowałem z SCFRench i Ru Hashą w oktawie.

I w końcu to działa: ale dokonałem pewnych modyfikacji

function message = makefuns
    assignin('base','fun1', @fun1);   % Ru Hasha
    assignin('base', 'fun2', @fun2);  % Ru Hasha
    message.fun1=@fun1;               % SCFrench
    message.fun2=@fun2;               % SCFrench
end

function y=fun1(x)
    y=x;
end

function z=fun2
    z=1;
end

Można wywołać w innym pliku „m”:

printf("%d\n", makefuns.fun1(123));
printf("%d\n", makefuns.fun2());

aktualizacja:

Dodałem odpowiedź, ponieważ ani +72, ani +20 nie działały dla mnie w oktawie. Ten, który napisałem, działa idealnie (i przetestowałem go w zeszły piątek, kiedy później napisałem post).

Gromph
źródło
2
Jeśli potrafisz wyjaśnić, czym różni się to od dwóch istniejących odpowiedzi, z których kopiujesz, usunę moją opinię. Przepraszam, że nie komentowałem wcześniej. Po prostu nie rozumiem, jak to jest inaczej, z wyjątkiem tego, że połączyłeś obie metody w jedną funkcję i dlatego robisz coś zbędnego. Proszę również wstawić odpowiednie linki do odpowiedzi, na które się powołujesz, „+72” i „+20” są dość tajemnicze, zajęło mi trochę czasu, zanim zdałem sobie sprawę, że odnosisz się do liczby głosów, które zmieniają się z czasem i robią twoje odniesienia niezrozumiały.
Cris Luengo