Parsowanie wzorów matematycznych na pole figur

0

Piszę taki programik dla siebie, w ramach rozrywki i nauki. Ma to liczyć pola figur. Zasadniczo proste, jeśli pola są zdefiniowane odgórnie, np.:

 def trojkat(a, h):
     pole = 0.5 * a * h
     return pole
 
 
 oblicz_trojkat = trojkat(5, 7)

Jednak ma to tę wadę, że figur jest mnóstwo a każda z nich może mieć wiele wzorów. To oznacza dziesiątki defów w kodzie. Ale to jeszcze nie jest najgorsze. Chciałbym, aby mój program się uczył - jeśli ktoś zna inny wzór, to może go podać. Tę część akurat mam już opanowaną - napisałem skrypt, który pyta użytkownika o nowy wzór, pobiera od niego parametry, jak kąt czy długości podstaw oraz wzór a następnie zapisuje to w słowniku, który na końcu zrzucany jest do jsona, który to z kolei otwiera się przy starcie, więc po następnym użyciu program ma już więcej wzorów.

Teoretycznie powinno być prościej - zamiast zyliarda definicji mam tylko jedną, która pobiera odpowiednie dane ze słownika, prosi użytkownika o podanie ich wartości a następnie liczy ze wzoru, który też jest w słowniku.

Przykładowy słownik wygląda tak, są tu tylko trzy przykładowe pola na trójkąt, resztę dopiszę, jak to zacznie działać:

slownik_z_polami = {'trojkat': [[['a', 'h'], ['0.5*a*h']], [['a', 'b', 'g'], ['0.5*a*b*sin(g)']], [['a', 'b', 'c', 'r'], ['0.5*r*(a+b+c)']]]}

Na czym polega mój problem?
Oczywiście, operując na słownikach i listach mogę pobrać z tego słownika dowolną wartość - wzór czy ilość parametrów. Mogę to wysłać do jednej zbiorczej funkcji, która to przemieli, zapyta użytkownika o wartości i wypluje dane. Tylko to są cały czas stringi. Jak sprawić, by to były liczby???
Np. wzór 0.5ah - zamiana przez int() nie ma sensu, bo program krzyczy, że nie ma takich zmiennych jak a i h.
Z kolei np. podstawa:

pobierz_dane1 = slownik_z_polami['trojkat'][0][0][0]

to tez string i choćby nie wiem co, nie zamienię go na int. Nawet jeśli wstępnie utworzę pobierz_dane_1 = 1 (czego wolałbym uniknąć, bo te dane numerowane mają być przydzielane dynamicznie w zależności od tego, ile ich jest, a tego przecież nie wiem - może ktoś podać wzór z dużą ilością parametrów, to nie będę na wszelki wypadek definiował 50ciu zmiennych), to po pobraniu ze słownika typ i tak się zmieni na str.

Jak to ugryźć???
Edit: hm, w podglądzie nie brakuje kwadratowego nawiasu po trójkącie, są też wcięcia, nie wiem, jak to tu prawidłowo wkleić, ale to dla idei nie ma chyba znaczenia.

Edit2: Może napiszę dla jasności, jak to powinno wg mnie działać. Już bez kodu - kod napisałem, ale nie działa, bo to stringi.
No więc widzę to tak:

def funkcja_liczenia_pola(jeden_parametr)

'''w trakcie rozmowy z programem wybiera się, jak się chce liczyć pole - np. z boków i sinusa kąta, to wystarczy, by określić ten jeden parametr, wg którego funkcja pobierze odpowiednie dane'''

  1. funkcja - wg tego jeden_parametr - pobiera z listy trzy wartości, a, b i g i prosi użytkownika o ich wartości.
    i już tu jest problem, choć np. slownik_z_polami['trojkat'][1][0][0] ma wartość 'a', to jest to string, nie przypiszę mu przez input żadnej wartości
  2. teraz funkcja pobiera wzór ze słownika i - mając podane przez użytkownika dane - oblicza pole i podaje wynik
    i tu jest analogiczny problem - wzór to string i nie wiem jak sprawić, żeby python traktował go jak wzór
50

Imo pakowanie tego do jednej funkcji i jakieś zapisywania po słownikach mija się z celem. Można zrobić to chociażby przy pomocy eval, który parsuje stringa na wyrażenie, jednak jest to zła praktyka i powinno się jej unikać. Dlaczego nie zrobić by tego obiektowo? Robisz sobie prosta abstrakcje i potem implementujesz pod konkretna figurę.

from typing import Protocol

class Figura(Protocol):
    def pole(self):
        """implementacja Pola"""

    def obwod(self):
        """implementacja Obwodu"""

class TrojkatProstokatny:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def pole(self):
        return (self.a*self.b) / 2

    def obwod(self):
        return self.a+self.b+self.c

Jasne, czytelne i proste do przetestowania.

Jak już bardzo chcesz bawić się w słowniki i jakieś dziwne automatyzacje to można wszystkie przez Ciebie stworzone obiekty spiąć w podobną hierarchię. Jednak ja bym odradzał, bo brzydko to wygląda :P

wzory = {
    "TrojkatProstokatny": {"args": ["a", "b", "c"], "obj": TrojkatProstokatny},
}

if __name__ == "__main__":
    wzory_info = '\n'.join(k for k in wzory)
    wzor = input(f"Mozliwe wzory:\n{wzory_info}\n: ")
    args = (int(input(f"Podaj wartosc dla {a}: ")) for a in wzory.get(wzor)["args"])
    obj = wzory.get(wzor)["obj"]
    figura: Figura = obj(*args)
    print(figura.obwod())
0

Wow! Dzięki wielkie, nie wiedziałem o czymś takim jak eval, jest to idealne rozwiązanie.

Dlaczego 'mija się z celem'??? Piszę programik jak najbardziej samowystarczalny, jeśli wiesz, co mam na myśli. Dlatego nie mogę pisać klasy dla każdej figury. Pomijając fakt, że jest ich bardzo dużo - to przecież nie znam wszystkich. A jak user będzie chciał pole ostrosłupa ściętego albo torusa? Albo wymyśli coś innego, co będzie potrzebne tylko jemu? Jak to wprowadzi do programu???
(Przyznaję, że szukam też takiego rozwiązania - ale dopiero zaczynam, więc nie pytam - aby móc, z poziomu konsoli, edytować kod. Żeby np. wprowadzać do kodu klasy, których tam nie ma. Na pewno AI to za dużo powiedziane, ale mój skrypt ma się uczyć - poznawać nowe wzory, wyrażenia, funkcje - nie może ich mieć predefiniowanych w kodzie. Oczywiście - gdybym pisał np. kalkulator dla kogoś - zrobiłbym, jak piszesz. Ale to taki fun dla mnie.)
Oczywiście tym userem będę tylko ja, ale życzę sobie, aby ten moduł (do liczenia) - był samowystarczalny - żebym go mógł niejako programować z poziomu konsoli, dodając kolejne figury czy nowe wzory do figur już istniejących. Bez ograniczeń - podaję dowolną nazwę i wzór i nie martwię się, że na to nie ma żadnej klasy.

Edit po drugim kodzie:
Oj tam, zaraz dziwne automatyzacje. Jeśli program ma się uczyć, to jak to inaczej zrobić?

Testuję Twój kod, na razie nie działa, nie wiem czemu, jak wpisuję liczbę, mam:

 TrojkatProstokatny
 : 8
 Traceback (most recent call last):
   File "C:\Users\Battery\PycharmProjects\Project\automatyzacje.py", line 31, in <module>
     args = (int(input(f"Podaj wartosc dla {a}: ")) for a in wzory.get(wzor)["args"])
 TypeError: 'NoneType' object is not subscriptable
46
manteusz napisał(a):

Wow! Dzięki wielkie, nie wiedziałem o czymś takim jak eval, jest to idealne rozwiązanie.

Dlaczego 'mija się z celem'??? Piszę programik jak najbardziej samowystarczalny, jeśli wiesz, co mam na myśli. Dlatego nie mogę pisać klasy dla każdej figury. Pomijając fakt, że jest ich bardzo dużo - to przecież nie znam wszystkich. A jak user będzie chciał pole ostrosłupa ściętego albo torusa? Albo wymyśli coś innego, co będzie potrzebne tylko jemu? Jak to wprowadzi do programu???
(Przyznaję, że szukam też takiego rozwiązania - ale dopiero zaczynam, więc nie pytam - aby móc, z poziomu konsoli, edytować kod. Żeby np. wprowadzać do kodu klasy, których tam nie ma. Na pewno AI to za dużo powiedziane, ale mój skrypt ma się uczyć - poznawać nowe wzory, wyrażenia, funkcje - nie może ich mieć predefiniowanych w kodzie. Oczywiście - gdybym pisał np. kalkulator dla kogoś - zrobiłbym, jak piszesz. Ale to taki fun dla mnie.)
Oczywiście tym userem będę tylko ja, ale życzę sobie, aby ten moduł (do liczenia) - był samowystarczalny - żebym go mógł niejako programować z poziomu konsoli, dodając kolejne figury czy nowe wzory do figur już istniejących. Bez ograniczeń - podaję dowolną nazwę i wzór i nie martwię się, że na to nie ma żadnej klasy.

Edit po drugim kodzie:
Oj tam, zaraz dziwne automatyzacje. Jeśli program ma się uczyć, to jak to inaczej zrobić?

eval jest przede wszystkim niebezpieczne i trudno debugowalne. W string można wsadzić wszystko stąd ciężko to kontrolować.. Poza tym jest wolne. Oczywiście do Twojego zadania wydaje się idealne i nadal możesz go użyć, jednak na dłuższa metę nie chciałbyś widzieć tej funkcji w kodzie produkcyjnym z powyższych powodów :D

0

To prawda, ale to bolączka każdego AI - mogę go okłamać, podając jakieś wymyślone słowo albo nawet swoje imię i takie AI będzie do mnie mówić per Mr.Dziobak. Z drugiej strony - jeśli podany wzór będzie błędny i program zwróci błąd - skrypt zaproponuje jego poprawę lub usunięcie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1