return gibt falschen wert zurück

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
THLC53
User
Beiträge: 9
Registriert: Mittwoch 16. November 2016, 11:22

Hallo :)

Ich habe ein Aufgabe in der ich eine funktion definieren soll mit der man Listen glätten kann.

als Beispielliste nehme ich [3,4,[[5]]] daraus soll dann diese Liste enstehen: [3,4,5]
Der Code gibt mir bei dem Aufruf von der funktion am Ende allerdings diese Liste: [[5]]
die neue Liste die ich mir erstellt habe "newlist" sieht wie folgt aus: [3,4,5] also so wie es seien sollte.
habe es auch mit print(newlist) getestet.
was ich nicht verstehe warum meine funktion diese Liste zurückgibt [[5]] und nicht [3,4,5]

Code: Alles auswählen

egg = [3,4,[[5]]]
newlist = []
def flatten(lst):
    global newlist
    for x in lst:
        while type(x) == list:
            flatten(x)
            return x
        else:
            newlist += [x]
    return newlist

print(flatten(egg))
hoffe mir kann jemand weiterhelfen.

lg THLC53
THLC53
User
Beiträge: 9
Registriert: Mittwoch 16. November 2016, 11:22

Habe jetzt mein Problem selber gelöst.

stehe aber jetzt vor einem anderen Problem

Code: Alles auswählen

egg = [3,4,[[5]]]
spam = [[[1, 2, egg], (6, [7]), 8], 9, False]
newlist = []
def flatten(lst):
    if type(lst) == list:
        for x in lst:
            flatten(x)
    if type(lst) != list:
        newlist.append(lst)
    return newlist
bei spam und egg kommt die richtige geglättete Liste raus.
das Problem vor dem ich noch stehe: Wie schaffe ich es das newlist nach aufruf der Funktion wieder eine leere Liste wird?
Führe ich 2 mal hintereinander flatten(lst) besteht das Ergebnis dann aus beiden Ergbnissen...
also flatten([3,4,5]) => [3,4,5]
flatten([1,2]) => [3,4,5,1,2]

hoffe man versteht was ich ich meine :)
BlackJack

@THLC53: Das ist keine ”echte” Funktion. Eine Funktion sollte nur auf Werten operieren die als Argumente übergeben wurden (Ausnahme: Konstanten). Und wenn sie einen Rückgabewert hat, dann sollte der die Funktion über ``return`` verlassen. Deine ”Funktion” verändert das externe `newlist`, was nicht sein sollte, weil das damit globaler Zustand ist. Und da `newlist` ausserhalb der Funktion bekannt ist, macht auch ein ``return newlist`` keinen Sinn, denn darauf könnte der Aufrufer ja auch so zugreifen.

Anstelle von `type()` sollte man `isinstance()` zum Testen auf Liste (und abgeleitete Typen) verwenden.

Wenn man gerne *eine* Liste erstellen möchte die dann gefüllt wird, würde man eine verschachtelte Funktion schreiben. Also eine Funktion in der erst die Liste neu erstellt wird und die dann eine innere, rekursive Funktion die diese Liste dann füllt und von der äusseren Funktion aufgerufen wird. Oder man schreibt eine rekursive Hilfsfunktion bei der die zu füllende Liste bei den rekursiven Aufrufen immer mit durchgereicht wird.

Wobei dieses füllen *einer* Liste, statt immer neue zu erstellen, schon eine Optimierung des normalen Ansatzes dar stell, in dem einfach bei jedem rekursiven Aufruf neue Listen erstellt werden.
THLC53
User
Beiträge: 9
Registriert: Mittwoch 16. November 2016, 11:22

Danke @BlackJack

meine funktion würde wie folgt aussehen(nach meinen tests funktioniert es auch):

Code: Alles auswählen

egg = [3,4,[[5]]]
spam = [[[1, 2, egg], (6, [7]), 8], 9, False]
def flatten(lst):
    newlist = []
    def flattenlist(lst):
        if isinstance(lst , list):
            for x in lst[:]:
                flattenlist(x)
        else:
            newlist.append(lst)
        return newlist
    return(flattenlist(lst))
Ist es jetzt eine echte Funktion oder?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@THLC53: die interne Funktion muß gar nichts zurückgeben, da sie ja auf der Liste, die in der übergeordneten Funktion definiert wurde, operiert. Die äußere Funktion kann dann direkt newlist zurückgeben, wobei die Klammern um das return überflüssig sind. Warum erzeugst Du immer Kopien Deiner Listen lst?

Alternativ kann man mit optionalen Parametern arbeiten:

Code: Alles auswählen

def flatten(iterable, result=None):
    if result is None:
        result = []
    for item in iterable:
        if isinstance(item, list):
            flatten(item, result)
        else:
            result.append(item)
    return result
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Man kann ein Objekt auch auf Iterierbarkeit testen. Als Grundlage dafür gibt es bereits eine vordefinierte Klasse im `collections`-Modul der Standardbibliothek. Dann braucht man die Spezialbehandlung nicht für einzelne bekannte Typen, sondern nur für Objekte, die sich wie ein `Iterable` verhalten oder von ihm abgeleitet sind. Eine mögliche Lösung sähe so aus:

Code: Alles auswählen

from collections import Iterable

def flatten(items):
    flattened = []
    for item in items:
        if isinstance(item, Iterable):
            flattened.extend(flatten(item))
        else:
            flattened.append(item)
    return flattened

# Alternativ als Generator (ab Python 3.3)
def flatten_gen(items):
    for item in items:
        if isinstance(item, Iterable):
            yield from flatten(item)
        else:
            yield item

def main():
    spam = [[[1, 2, egg], (6, [7]), 8], 9, False]
    print(flatten(spam))
    gen = flatten_gen(spam)
    print(list(gen))

if __name__ == '__main__':
    main()
BlackJack

@snafu: Wobei sich das dann aber auch anders verhält und auch Tupel "platt macht" *und* Du solltest das mal mit Zeichenketten ausprobieren. Auf der anderen Seite kann man so trotzdem nicht *alle* iterierbaren Objekte erkennen, denn der Test funktioniert nicht bei allen Objekten.
THLC53
User
Beiträge: 9
Registriert: Mittwoch 16. November 2016, 11:22

Hier noch die Aufgabenstellung, habe ne Kopie der Liste genommen, da doch sonst die Liste verändert wird oder?
Ich bin gerade bei Teilaufgabe a).

Danke mal wieder für die Hilfe in dem Forum! :)

was ist eigentlich damit gemeint bei Aufgabe b) ? Das die Liste sich selbst glätten soll, in gegensatz zu a).

macht es nicht schon das bei meiner funktion? :?

Bild
THLC53
User
Beiträge: 9
Registriert: Mittwoch 16. November 2016, 11:22

@Sirius3
wenn ich in der Internen Funktion das return newlist weglasse bekomme ich kein Ergebnis beim Aufruf der Funktion
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@THLC53:
Warum denkst du, dass die Liste verändert wird, wenn man nicht über die Kopie geht? Das bloße Durchlaufen einer Liste führt keine Veränderung an der Liste durch. Das Übergeben an eine Funktion verändert die Liste ebenfalls nicht, solange die Funktion sie nicht nicht verändert. Und da man in Python bevorzugt die veränderten Inhalte als neue Liste ausgibt (so wie du es ja in a) machen sollst), muss man sich in der Regel auch keine Sorgen machen, wenn man die Liste an andere Funktionen übergibt. In b) sollt ihr aber genau dies machen (also Veränderung immer direkt in die übergebene Liste schreiben), sodass dann auch kein `return`-Statement nötig wäre.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Für a) habe ich quasi schon eine Lösung geliefert, nur dass du eben nicht mit dem `collections`-Modul arbeiten darfst. Und BlackJack hatte ja bereits angemerkt, dass ein zu allgemeiner Typ auch zu Nachteilen führen kann. Also ist die Beschränkung auf Listen wahrscheinlich sowieso das Sinnvollste.
BlackJack

Mal spasseshalber eine Lösung in JavaScript, mit der kleinen Einschränkung das ich aus dem Tupel ein Objekt gemacht habe, weil JavaScript ja keine Tupel kennt:

Code: Alles auswählen

'use strict';
var util = require('util');

var logObject = function (obj) {
  console.log(util.inspect(obj, {depth: null}));
};

var flatten = function (items) {
  if (Array.isArray(items)) {
    var result = [];
    for (var i = 0; i < items.length; i++) {
      result.push.apply(result, flatten(items[i]));
    }
    return result;
  } else {
    return [items];
  }
};

var flattenInPlace = function (items) {
  var result = [];
  var flatten = function (items) {
    if (Array.isArray(items)) {
      for (var i = 0; i < items.length; i++) {
        flatten(items[i]);
      }
    } else {
      result.push(items);
    }
  };
  flatten(items);
  items.splice.apply(items, [0, items.length].concat(result));
};

var main = function () {
  var egg = [3, 4, [[5]]];
  var spam = [[[1, 2, egg], {a: 6, b: [7]}, 8], 9, false];

  logObject(flatten(egg));
  logObject(flatten(spam));

  flattenInPlace(egg);
  logObject(egg);
  logObject(spam);

  flattenInPlace(spam);
  logObject(spam);
};

if (require.main === module) {
  main();
}
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Obligatorische Erlang-Lösung:

[codebox=erlang file=Unbenannt.txt]-module(fl).

%% API
-export([flatten/1]).


flatten(X) ->
flatten(X, []).

flatten([H | T], L) ->
flatten(H, [T | L]);

flatten([], [H | T]) ->
flatten(H, T);

flatten([], []) ->
[];

flatten(E, L) ->
if
is_tuple(E) -> flatten(tuple_to_list(E), L);
true -> [E | flatten(L, [])]
end.[/code]

[codebox=erlang file=Unbenannt.txt]1> Spam = [[[1,2], {6, [7]}, 8], 9, false].
[[[1,2],{6,[7]},8],9,false]
2> c(fl).
{ok,fl}
3> fl:flatten(Spam).
[1,2,6,7,8,9,false]
[/code]

b) ist natürlich nicht in Erlang implementierbar.
the more they change the more they stay the same
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Na, was wohl? :)[codebox=prolog file=Unbenannt.txt]flatten([], []).
flatten([H|T], Out) :-
flatten_dl([H|T], Out - []).

flatten_dl([H|T], L2 - L0) :-
!,
flatten_dl(T, L1 - L0),
flatten_dl(H, L2 - L1).
flatten_dl([], L - L) :- !.
flatten_dl(H, [H|L] - L).[/code]Ergebnis:[codebox=prolog file=Unbenannt.txt]?- flatten([1, 2, [[3], [4, 5], 6]], L).
L = [1, 2, 3, 4, 5, 6].[/code]Oder iterativ mittels Continuations:[codebox=prolog file=Unbenannt.txt]flatten([], []).
flatten([H|T], Out) :-
flatten_dl([H|T], Out - [], true).

flatten_dl([H|T], L2 - L0, Cont) :-
!, flatten_dl(T, L1 - L0, flatten_dl(H, L2 - L1, Cont)).
flatten_dl([], L - L, Cont) :-
!, call(Cont).
flatten_dl(H, [H|L] - L, Cont) :-
!, call(Cont).
[/code]Gleiches Ergebnis.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@Dav1d: Das Tupel wird im Beispiel in der Aufgabe aber nicht ”geplättet”. Das gibt Punktabzug. ;-)
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ahhh und ich hab mir extra nach Mühe gegeben, um auch Tuple zu plätten .. mal wieder: wer lesen kann löst Aufgaben besser :evil:
the more they change the more they stay the same
BlackJack

Die Freuden von sicheren, statisch typisierten Sprachen:
[codebox=text file=Unbenannt.txt]#[derive(Debug,Clone)]
enum Item {
Vector(Vec<Item>),
Number(u32),
Tupel((Box<Item>, Box<Item>)),
Bool(bool),
}

use Item::*;

fn flatten(item: Item) -> Item {
match item {
Vector(items) => {
let mut result: Vec<Item> = Vec::with_capacity(items.len());
for item in items {
match flatten(item) {
Vector(mut flattened) => result.append(&mut flattened),
x @ _ => panic!("unexpected item {:?}", x),
}
}
Vector(result)
},
_ => Vector(vec![item]),
}
}

fn main() {
let egg = Vector(vec![
Number(3), Number(4), Vector(vec![Vector(vec![Number(5)])])
]);
let spam = Vector(vec![
Vector(vec![
Vector(vec![Number(1), Number(2), egg.clone()]),
Tupel((Box::new(Number(6)), Box::new(Vector(vec![Number(7)])))),
Number(8)
]),
Number(9),
Bool(false)
]);
println!("{:?}", egg);
println!("{:?}", spam);

println!("\n{:?}", flatten(egg));
println!("{:?}", flatten(spam));
}[/code]
Nur a) weil Rust einen stäkeren Fokus auf funktionale Programmierung legt, und das ganze mit Zustand noch umständlicher zu schreiben wäre. :-)
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ich bin mir nicht sicher was ich von Rust halten soll, manche Sachen finde ich genial aber irgendwie kommt es mir so vor als würde sich die Sprache gegen einen wenden wenn man tatsächlich mit ihr Code schreiben will.

Und warum kann die dumme Codebox kein Rust :evil:
the more they change the more they stay the same
BlackJack

@Dav1d: Was das hier so unschön macht, ist ja hauptsächlich das so viele verschiedene Datentypen nahezu beliebig verschachtelt werden können. Was bei dynamisch typisierten Sprachen halt deutlich weniger Probleme macht.

Geshi, der Syntaxhighlighter der vom Forum verwendet wird, ”kann” in seiner aktuellen Version zwar Rust, aber leider alles andere als fehlerfrei.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Clojure:[codebox=clojure file=Unbenannt.txt](ns fl.core)

(defn flat- [li]
(cond
(not (list? li)) (list li)
(empty? li) ()
:else (concat (flat- (first li)) (flat- (rest li)))))

(defn flat [li]
(if (list? li)
(flat- li)
li))

(defn -main [& args]
(print (flat '(1 2 ((3 4) ((5) 6))))))[/code]Ergebnis:[codebox=bash file=Unbenannt.bsh]$ lein run
(1 2 3 4 5 6)[/code]
In specifications, Murphy's Law supersedes Ohm's.
Antworten