Bir tamsayı bir aralığa nasıl kenetlenir?


94

Takip koduna sahibim:

new_index = index + offset
if new_index < 0:
    new_index = 0
if new_index >= len(mylist):
    new_index = len(mylist) - 1
return mylist[new_index]

Temel olarak, yeni bir indeks hesaplıyorum ve bunu bir listeden bir eleman bulmak için kullanıyorum. Dizinin listenin sınırları içinde olduğundan emin olmak için, bu 2 ififadeyi 4 satıra yayılmış olarak yazmam gerekiyordu. Bu oldukça ayrıntılı, biraz çirkin ... Söyleyeyim, bu oldukça pitonik değil .

Daha basit ve daha kompakt başka bir çözüm var mı? (ve daha fazla pitonik )

Evet, if elsetek satırda kullanabileceğimi biliyorum ama okunabilir değil:

new_index = 0 if new_index < 0 else len(mylist) - 1 if new_index >= len(mylist) else new_index

Ben de zincirini yapabileceğini biliyorum max()ve min()birlikte. Daha kompakt, ancak biraz belirsiz olduğunu hissediyorum, yanlış yazarsam hataları bulmanın daha zor olduğunu düşünüyorum. Başka bir deyişle, bunu çok basit bulmuyorum.

new_index = max(0, min(new_index, len(mylist)-1))

2
Eğer "biraz belirsiz" geliyorsa, bunun dışında bir işlev mi yapacaksınız?
Noel Baba

1
Evet, bir fonksiyon yazabilirim ama konu bu değil. Soru, bunun nasıl uygulanacağıdır (satır içi veya bir işlevde).
Denilson Sá Maia

clamp = lambda value, minv, maxv: max(min(value, maxv), minv)Dan API kullanarak arma.sourceforge.net/docs.html#clamp
Dima Tisnek

Yanıtlar:


120

Aslında bu oldukça açık. Çoğu insan bunu çabucak öğrenir. Onlara yardım etmek için bir yorum kullanabilirsiniz.

new_index = max(0, min(new_index, len(mylist)-1))

12
Olması gerektiği kadar pitonik olmadığını düşünmeme rağmen, bunun şu anda sahip olduğumuz en iyi çözüm olduğunu da hissediyorum.
Denilson Sá Maia

50
def clamp(n, smallest, largest): return max(smallest, min(n, largest))
csl

3
@csl Folks her zaman bu küçük yardımcı işlevleri sağlar, ancak onları nereye koyacağımı asla bilemiyorum. helperFunctions.py? Ayrı bir modül mü? Ya bu tamamen farklı şeyler için çeşitli "yardımcı işlevler" ile dolaşırsa?
Mateen Ulhaq

2
Bilmiyorum, ama bunların çoğunu toplar ve mantıklı modüller halinde sınıflandırırsanız, neden GitHub'a koyup ondan bir PyPi paketi oluşturmuyorsunuz? Muhtemelen popüler olur.
csl

@MateenUlhaqutils.py
Wouterr

90
sorted((minval, value, maxval))[1]

Örneğin:

>>> minval=3
>>> maxval=7
>>> for value in range(10):
...   print sorted((minval, value, maxval))[1]
... 
3
3
3
3
4
5
6
7
7
7

12
Yerleşiklerin yaratıcı kullanımı için +1 sorted(). Oldukça kompakt, ancak biraz belirsiz. Her neyse, diğer yaratıcı çözümleri görmek her zaman güzeldir!
Denilson Sá Maia

12
Çok yaratıcı ve aslında min(max())inşaat kadar hızlı . Numaranın aralık dahilinde olması ve değiş tokuşa gerek olmaması durumunda çok az daha hızlı.
kindall

41

burada birçok ilginç cevap, hepsi aynı, sadece ... hangisi daha hızlı?

import numpy
np_clip = numpy.clip
mm_clip = lambda x, l, u: max(l, min(u, x))
s_clip = lambda x, l, u: sorted((x, l, u))[1]
py_clip = lambda x, l, u: l if x < l else u if x > u else x
>>> import random
>>> rrange = random.randrange
>>> %timeit mm_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.02 µs per loop

>>> %timeit s_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 1.21 µs per loop

>>> %timeit np_clip(rrange(100), 10, 90)
100000 loops, best of 3: 6.12 µs per loop

>>> %timeit py_clip(rrange(100), 10, 90)
1000000 loops, best of 3: 783 ns per loop

paxdiablo'da var !, düz ol 'python kullanın. Uyuşuk versiyon, belki de şaşırtıcı olmayan bir şekilde, partinin en yavaş olanıdır. Muhtemelen, diğer sürümlerin sadece kendi argümanlarını sıraladıkları dizileri aradığı içindir.


7
@LenarHoyt, Numpy'nin performansının tek sayılar değil, büyük diziler etrafında tasarlandığını düşünürsek, bu şaşırtıcı değil. Ayrıca, önce tamsayıyı dahili bir veri türüne dönüştürmesi gerekir ve birkaç farklı türde girdiyi kabul ettiğinden, girdinin ne tür olduğunu ve neye dönüştürüleceğini anlamak muhtemelen önemli ölçüde zaman alır. Birkaç bin değerden oluşan bir diziyi (tercihen ilk dönüştürmesi gereken bir liste veya demet değil) beslerseniz, çok daha iyi Numpy performansı göreceksiniz.
blubberdiblub

Python, üç kat daha yavaştır. 783 ns = 783.000 µs. Geçmişte de aynı hatayı yaptım. Gösterim ince.
Dustin Andrews

7
@DustinAndrews bunu tersine çeviriyorsun. 1 µs, 10 ^ -6 saniye, 1 ns, 10 ^ -9 saniyedir. python örneği 1 döngüyü 0.784 µs'de tamamlar. Ya da en azından, test ettiğim makinede yaptı. Bu mikro ölçüt, diğer mikro ölçütler kadar yararlıdır; sizi gerçekten kötü fikirlerden uzaklaştırabilir, ancak muhtemelen yararlı kod yazmanın en hızlı yolunu bulmanıza yardımcı olmayacaktır .
SingleNegationElimination

İşlevlerin çağrısında hafif bir ek yük var. Kriterleri yapmadım, ancak PyPy gibi JIT derleyicisini kullanırsanız bu oldukça olası mm_clipve py_clipeşit derecede hızlı olacaktır. İlkinin daha okunabilir olması ve Python'un felsefesinde okunabilirliğin çoğu zaman hafif bir performans kazanımından daha önemli olması dışında.
Highstaker

@DustinAndrews Gerçeğe dayalı yanlış yorumunuzu silmenizi tavsiye ederim çünkü geriye doğru anladınız.
Acumenus

38

Numpy.clip'e bakın :

index = numpy.clip(index, 0, len(my_list) - 1)

Dokümanlar , ilk parametresinin "kırpılacak öğeleri içeren bir dizi" clipolduğunu söylüyor a. Yani yazmak zorundasın numpy.clip([index], …, değil numpy.clip(index, ….
Rory O'Kane

13
@ RoryO'Kane: Bunu denedin mi?
Neil G

1
Pandalar, buna Seri ve Veri Çerçeveleri ve Panellerde de izin verir.
Nour Kurt

18

Zincirleme max()ve min()birlikte, gördüğüm normal deyimdir. Okumakta zorlanıyorsanız, işlemi özetlemek için bir yardımcı işlev yazın:

def clamp(minimum, x, maximum):
    return max(minimum, min(x, maximum))

14

Sevgili okunabilir Python dilime ne oldu? :-)

Cidden, onu bir işlev haline getirin:

def addInRange(val, add, minval, maxval):
    newval = val + add
    if newval < minval: return minval
    if newval > maxval: return maxval
    return newval

sonra şöyle bir şey söyleyin:

val = addInRange(val, 7, 0, 42)

Veya hesaplamayı kendiniz yaptığınız daha basit, daha esnek bir çözüm:

def restrict(val, minval, maxval):
    if val < minval: return minval
    if val > maxval: return maxval
    return val

x = restrict(x+10, 0, 42)

İsterseniz, min / maks. Bir liste bile yapabilirsiniz, böylece daha "matematiksel olarak saf" görünebilir:

x = restrict(val+7, [0, 42])

6
Bunu bir fonksiyona koymak iyidir (ve bunu çok yapıyorsanız tavsiye edilir), ancak bence minve maxbir grup koşullu ifadeden çok daha net. (Ne için olduğunu bilmiyorum add- sadece söyle clamp(val + 7, 0, 42).)
Glenn Maynard

1
@GlennMaynard. Min ve max'ın daha temiz olduğunu kabul edebileceğimden emin değilim. Bunları kullanmanın tüm amacı, tek bir satıra daha fazlasını doldurarak kodu daha az okunaklı hale getirmektir.
Mad Fizikçi

10

Bu bana daha pitonik görünüyor:

>>> def clip(val, min_, max_):
...     return min_ if val < min_ else max_ if val > max_ else val

Birkaç test:

>>> clip(5, 2, 7)
5
>>> clip(1, 2, 7)
2
>>> clip(8, 2, 7)
7

8

Kodunuz çok hantal görünüyorsa, bir işlev yardımcı olabilir:

def clamp(minvalue, value, maxvalue):
    return max(minvalue, min(value, maxvalue))

new_index = clamp(0, new_index, len(mylist)-1)

2

Kodunuzu karıştıracağından, sık sık uygulamadığınız sürece bu tür küçük görevler için işlev yazmaktan kaçının.

bireysel değerler için:

min(clamp_max, max(clamp_min, value))

değer listeleri için:

map(lambda x: min(clamp_max, max(clamp_min, x)), values)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.