Zastanawiałem się, jak zająć się projektowaniem literału „idealnego” zakresu, gdybym zaprojektował język. Dla tych, którzy nie znają literału zakresu w instrukcji reprezentującej zakres wartości, np. 1-4. Są najczęściej używane w pętlach for / foreach
Wydaje się, że należy wziąć pod uwagę kilka kwestii
Obsługa przedziałów włączających i wyłącznych, przypisywanie +1 lub -1 do punktów końcowych wydaje się nieco nieporadne i podatne na błędy.
Obsługa stopniowania, dzięki czemu można na przykład ustawić zakres liczb parzystych lub nieparzystych
Czytelność, powinno być oczywiste, co opisuje literał zakresu
Jednoznaczność, powinno być całkowicie niedwuznaczne, co opisuje literał zakresu
Domyślnie powinna to być wartość od włączającej do wyłącznej, ponieważ w większości przypadków używa się jej do zapętlania tablic itp.
Tak czy inaczej, jednym z przykładów literału zakresu, jaki widziałem, jest Ruby, który ma postać 1..3 dla wyłącznego (na końcu) zakresu i 1 ... 3 dla włącznie (na końcu). Możesz także wykonać 1..10. krok (5). Po dokładnym rozważeniu znalazłem jednak kilka rzeczy, które nie podobały mi się w tym podejściu (z mojej ograniczonej znajomości rubinu)
Możesz opisać tylko i wyłącznie na koniec. Opisywanie większości scenariuszy wydaje się nieco niespójne.
Różni się tylko o dodatkowy. wydaje się być przepisem na utrudnienie sprawdzenia, czy dany zakres jest włączający, czy wyłączny. Nie wiem o tobie, ale kropki stają się czasem rozmazane :)
Dodanie metody takiej jak notacja dla zakresów wydaje się mieszać pojęcie literału z pojęciem klasy, co wydaje się nieco niespójne (nawet jeśli zakresy zostaną skompilowane do klasy)
W każdym razie po rozważeniu różnych alternatyw. Wymyśliłem to
- [5..1] 5,4,3,2,1
- [1..5 [ 1,2,3,4
- ] 1..5] 2,3,4,5
- [ 0..5..20] 0,5,10,15,20
i tak dalej. Podoba mi się, ponieważ [normalnie denonuje zestaw i ten rodzaj pasuje do tego, nawet jeśli byłoby to inaczej niż zestaw.
Jedną z rzeczy, które mnie trochę podrażniają, jest wprowadzenie obowiązkowych wskaźników wykluczających / włączających, tj. Jeśli napiszesz tylko 1..5, domyślnie wyniesie 1,2,3,4, ponieważ jest to najczęstszy przypadek z tablicami itp. Jest łatwiejszy i bardziej czytelny, ale mniej konkretny, a gdybyś musiał napisać [1..5 [wcześnie] dowiadujesz się, jak działają.
Jak myślisz, czy obejmowałem większość baz, przeoczyłem coś? czy uczyniłbyś [] obowiązkowym? Czy projektowałbyś literały zasięgu inaczej w swoim języku programowania?
Kandydaci
- styl nawiasu: [0..10 [ , z krokiem: [0..5..20 [
- notacja interwałowa: [0..10) z krokiem: [0..5..20)
- wykrzyknik na wyłączność. 0 ..! 10, z krokiem: 0..5 ..! 20
- z innym krokiem. 0 ..! 20, 5
- sprawiłoby to, że domyślna * 0..10 ' włącznie
- wordy: [0 do! 20 na 5]
Muszę powiedzieć, że moim ulubionym do tej pory pod względem estetycznym jest 0 ..! 10 i 0..5 ..! 20 , chciałbym tylko, aby domyślna wartość 0..10 obejmująca wyłącznie była bardziej logiczna
1,5,10,15,20
Luka 4, 5, 5, 5 ?!Odpowiedzi:
Po co wymyślać to, co już istnieje w matematyce?
Notacja interwałowa
Powinno to obejmować punkt „jednoznaczności”, ponieważ już istnieje.
Twoim jedynym problemem byłoby zdefiniowanie stopniowania. Może coś innego w matematyce może w tym pomóc?
źródło
[1..5[
) dezorientuje redaktorów przynajmniej tak bardzo, jak standardowy zapis interwału.!
do wykluczania pierwszego lub ostatniego elementu może być dobre.Widziałeś zakresy Haskell? Ich pomysł na kroki jest podobny do twojego (w środku), ale zaznaczony różnicą składniową (od odpowiedzi @ luqui ):
Uważam tę notację za bardzo intuicyjną: zaczynasz wyliczać i przeskakiwać nad ciałem, aby zaznaczyć, gdzie powinno się kończyć.
Nie obejmuje przypadku „wyłączności”, ale szczerze mówiąc wolałbym jednoznacznie powiedzieć o tym zachowaniu (określić, która granica jest włączająca, a która wyłączna), i oczekuje od użytkownika wprowadzenia zmian, chyba że składnia jest wyjątkowo jasna (i nie myli redaktorów agnostycznych z językiem).
źródło
Powinieneś przeczytać o listach w językach, które je oferują.
Na przykład Python całkiem ładnie omawia twoje przypadki dzięki
range()
funkcji i zrozumieniu listy dla spraw zbyt skomplikowanych, by je wyrazićrange()
.źródło
Wierzę, że twoja notacja jest daleka od bycia jednoznacznym. Intuicyjnie,
[0..5..20]
nie wygląda mi to na zakres o szerokości kroku = 5. W niektórych przypadkach narożnych nawet się nie udaje: co z wyrażeniem zestawu (0,2) -[0..2..2]
lub co powinienem umieścić na środku?Myślę, że notacja plastra Pythona jest solidnym podejściem:
[start:end:step]
(krok jest opcjonalny).Jeśli naprawdę potrzebujesz wsparcia dla zakresów wyłącznych i włączających, użyłbym standardowej notacji zakresu (tj. Używając
(
i[
), chyba że wprowadzi to dwuznaczności składniowe w twoim języku.źródło
[]
,[[
,]]
i][
, bez nawiasu ... i nasz standard jest lepiej, oczywiście;)Myślę, że kiedy ustawiasz trzecią wartość przyrostu, lepiej jest dodać ją do końca, a nie do środka, ponieważ jest to wartość opcjonalna i wydaje się bardziej intuicyjna dla doświadczonych programistów niż dodawanie opcjonalnego trzeciego argumentu w środku .
więc zamiast [1..5..20] możesz zrobić coś takiego jak [1..20] [5] lub [1..20,5]
źródło
Dlaczego nie użyć po prostu [1..4], [2..5], [2..4]? Wykluczenie zakresów nie oferuje dużych korzyści. Nie musisz pisać MIN + 1 ani MAX-1, tak, ale i tak tego nie zabraniają. Zbyt duża różnorodność, imho. Mój wybrany język, scala, ma (1 do 3) i (1 do 3) [= (1 do 2)], ale na początku tylko mnie pomylił. Teraz zawsze używam „x do y” i dlatego wiem, że opcja, której nie używam, jest wykluczająca, ale oczywiście nie korzystam z opcji, której nie używam.
jest oczywiście (1 do MAKS.) (x => y = (MAKS. + 1) - x), ale jest to duża płyta kotłowa i nie jest przyjazna dla użytkownika.
jest oczywiście (od 0 do 4) (x => y = x * 5) to nie tyle płyty kotłowej. Sporny.
źródło
Co powiesz na coś takiego:
i
Ae
w drugiej pary nawiasach kwadratowych wskazuje, czy początek lub zakończenie zakresie jest włącznie lub wyłączne (lub można użyćincl
, aexcl
jeśli chcesz być bardziej jasne).by 5
wskazuje przedział impulsowania. Pierwszy i ostatni przykład zawierają pierwszą i ostatnią wartość, więc pominąłem[i,i]
, co moim zdaniem byłoby OK zakładanym domyślnym zachowaniem.Wartości domyślne mogą dotyczyć
[i,i]
specyfikatora włączającego / wyłącznego iby 1
specyfikatora kroku.Normalnie sugerowałbym standardową notację obejmującą
(
i,[
jak wspomniano @Dan McGrath, ale zgadzam się, że w kodzie może to wyglądać na mylące.źródło
A zwycięzcą jest
Przepraszam, że wybrałem własne rozwiązanie. Naprawdę doceniam wszystkie komentarze i opinie. Proszę o krytykę tego rozwiązania, jeśli znajdziesz coś złego
Więc zdecydowałem się na:
Jak widać, włączanie jest domyślne dla obu zmysłów, dlatego intuicyjnie jest najbardziej sensowne, szczególnie podczas korzystania ze stepowania. Możesz użyć ! aby koniec był wyjątkowy.
Minusem jest to, że zwykle chcesz używać wyłączności podczas pracy przeciwko macierzy, ale z drugiej strony, w większości przypadków prawdopodobnie powinieneś użyć czegoś takiego jak for-each w takich przypadkach. Jeśli naprawdę potrzebujesz pracy z indeksem, analizator składni może rozpoznać, że prawdopodobnie zapomniałeś znacznika wykluczenia na końcu i wygenerował ostrzeżenie
Poszedłem z użyciem ... po obu stronach zmiennej step zamiast przecinkiem lub czymkolwiek innym, ponieważ to właśnie wyglądało najczystiej i mam nadzieję, że jest najbardziej czytelne.
Wrzuciłem też składnię z przecinkami, aby móc tworzyć zakresy, w których różne części mają różne kroki
źródło
Nie użyłbym formularza „[1..5 [”] z tego samego powodu, dla którego uniknąłbyś mieszania parens i nawiasów klamrowych - pomyliłoby to dopasowanie nawiasów większości istniejących edytorów. Być może użyj znacznika wewnątrz nawiasów klamrowych, na przykład „[1 .. | 5]”.
Inną rzeczą do rozważenia jest zachowanie metod w celu uzyskania limitów. Na przykład „początek” / „koniec” vs. „pierwszy” / „ostatni” vs. „min” / „maksymalny”. Muszą być rozsądni zarówno w przypadku limitów włączających i wyłącznych, jak i tych o rozmiarze kroku.
źródło
Ruby ma notację i działający obiekt Range . Zasadniczo wygląda to tak:
W Ruby wielkość kroku nie jest funkcją zakresu, ale raczej iteracją w całym zakresie. Możemy użyć tego samego zakresu powyżej i iterować go inaczej, jeśli chcemy:
Oto rzeczy, które warto rozważyć:
BTW, zakresy są raczej miłe, jeśli pozwalasz na ich uwzględnienie w instrukcji switch, tak jak robi to Ruby:
Nie twierdzę, że obiekt zakresu Ruby jest końcem wszystkiego i bądź wszystkim, ale jest to działająca implementacja koncepcji, o której mówisz. Graj z nim, aby zobaczyć, co lubisz, a czego nie, i co robisz inaczej.
źródło
Anyways, one example of range literal I've seen is Ruby which is in the form of 1..3 for an exclusive (on the end) range and 1...3 for inclusive (on the end). You can also do 1..10.step(5).
Jednym z rozwiązań, które z pewnością rozważę, jest użycie przeciążenia operatora, aby umożliwić np
Niekoniecznie nie byłby natychmiast przejrzysty dla wszystkich, ale kiedy przestaną i pomyślą, że mam nadzieję, że większość programistów to zrozumie, a ludzie, którzy regularnie używają tego języka, po prostu nauczą się go jako idiomu.
Aby to wyjaśnić, wynikiem byłoby
[1, 6, 11, 16]
podniesienie mnożenia, a następnie dodawanie ponad zakres. Nie zdawałem sobie sprawy, że użytkownicy Pythona mogą być niejednoznaczni; Myślę o zakresach jako o podzestawach całkowitych zamówień, a nie o listach. a powtarzanie zestawu nie ma sensu.źródło
list expression * 5
może również oznaczać „powtórz wartośćlist expression
5 razy”, jak ma to miejsce w Pythonie.1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3
?1,3,6,9,12
?5,10,15
? może coś jeszcze? Tak czy inaczej, uważam, że ten zapis jest całkowicie sprzeczny z intuicją. Wygląda na to, że to może jakiś dziwny operacji wskaźnik C ...